cloudevents / spec

CloudEvents Specification
https://cloudevents.io
Apache License 2.0
5.06k stars 583 forks source link

Message Producer Signature #565

Open jkozlick opened 4 years ago

jkozlick commented 4 years ago

I was reading through some of the issues that have been closed, namely #378 and #379, so sorry if this is a duplicate issue.

Using Cloudevents to publish events across systems where the publisher does not control the consumer endpoint seems to be problematic, as there is no way to verify within context attributes that the producer is actually the one that is sending the message. Is there a proposal being discussed on adding something along the lines of a "eventsignature" field within the the context attributes so that an event can be verified as coming from a specific producer without having to unpack the event data to do so. Would this be better served by extension context attributes?

duglin commented 4 years ago

This topic has come up a few times and each time the group decided to defer it. Sometimes out of fear of the work becoming what WS-Security turned into, or because it was thought that it would be better handled by a layer on top of CloudEvents - e.g. perhaps via a secondary spec/extension as you suggested.

IMO I think the group might be open to discussing this as long as it doesn't block any of the existing work efforts. However, I think if someone really wanted to drive this then it would be best to start with a concrete proposal (even if everyone hates it 😄 ) because then it'll focus the discussion and not turn into endless rat-holes.

Anyone else have any more thoughts on this?

jkozlick commented 4 years ago

Been a while since I made this comment, but the group that I was working with solved this by creating a canonical version of the event, signing it and using a JWT to pass the signed claims.

This has worked well, and allowed us to stay complaint with the Cloudevents specification without having to introduce any secondary specifications or extensions.

I can outline this in more detail if anyone is interested, but I am OK with this issue being closed if no one else has any comment.

duglin commented 4 years ago

@jkozlick thanks - I think I'll leave it open for a while and tag it for post v1.0 work to remind us to look it over.

duglin commented 3 years ago

Been a while - I'd like to suggest that we close this one. Any objections?

erikerikson commented 3 years ago

Hi @jkozlick, I am curious if you've seen #770 or might have thoughts to add there? A comparison might be valuable. Perhaps better would be: how might your approach have been supported or hindered by that extension?

cicorias commented 1 year ago

@jkozlick

Been a while since I made this comment, but the group that I was working with solved this by creating a canonical version of the event, signing it and using a JWT to pass the signed claims.

This has worked well, and allowed us to stay complaint with the Cloudevents specification without having to introduce any secondary specifications or extensions.

I can outline this in more detail if anyone is interested, but I am OK with this issue being closed if no one else has any comment.

Could you share your approach here? Or DM me direct?

afrittoli commented 1 year ago

I'm also interested about producer signatures, especially in the context of CDEvents.

CDEvents is a project that aims bring interoperability in the continuous delivery space by standardising the payload for events produced by various tools. CDEvents' default binding is CloudEvents. In the context of CDEvents, one of the use cases we consider is storing all events in an "evidence store" and using that data to produce an audit trail for an artifact as well as take decisions about the next step in the workflow based on what has happened so far. To safely take decisions based on data collected from events, one needs to be able to trust that the source of the event is indeed the one specified in the source and that the content of the event has not been tampered with.

A solution like the one proposed by @jkozlick could work, but it's a risk for interoperability unless it's included in the CloudEvents spec, because a consumer would then need to support every producer-specific approach to signing events which is not what we want.

I wanted to gauge what kind of interest exists in the CloudEvents community about this feature, this is something we care about in CDEvents so I would be happy to collaborate on this, perhaps resurrect #770 and continue from there.

duglin commented 1 year ago

@afrittoli follow the breadcrumbs here: https://github.com/cloudevents/spec/pull/1102 and see if anything in there is something that you'd like to follow-up on. To me the biggest take-away from that chat is that people are open to something in this space, we just need to find the right technical solution.

MaerF0x0 commented 2 months ago

I had a similar need and scratched the itch like with these snippets. I havent implemented the signature validator on the consumer side yet, so tbd if I did it wrong. No warranties on the crypto, I'm not an expert!

This solution does not solve for key management though. You have to figure out how to give producers the private key and consumers the public key. Don't just hard code them to your repo!!

It signs across the data and stuffs the signature into event data. The consumer will need to get the data, read the signature, and then validate the signature across the data sans signature key.


// withSignature configures with a signer
func withSignature(ed25519Key *ed25519.PrivateKey) client.Option {
    return client.WithEventDefaulter(EventSigner(ed25519Key))
}

func EventSigner(ed25519Key *ed25519.PrivateKey) client.EventDefaulter {
    return func(ctx context.Context, evt event.Event) event.Event {
        evt.Context = evt.Context.Clone()
        dataBytes := evt.Data()
        hasher := sha512.New()
        hasher.Write(dataBytes)
        hashed := hasher.Sum(nil)
        var signature []byte
        var err error
        if ed25519Key != nil {
            signature, err = ed25519Key.Sign(nil, hashed, &ed25519.Options{})
            if err != nil {
                panic("ed25519 wtf- " + err.Error())
            }
        } else {
            panic("no key provided for signing")
        }
        data := make(map[string]interface{})
        evt.DataAs(&data)
        data["signature"] = signature
        evt.SetData(cloudevents.ApplicationJSON, data)
        return evt
    }
}

    c, err := cloudevents.NewClient(sender,
        withSignature(cfg.cfg.ED25519SigningKey), 
        )