parallaxsecond / parsec-book

Parsec documentation
https://parallaxsecond.github.io/parsec-book
Apache License 2.0
12 stars 17 forks source link

Create a chapter that defines the rules for stable releases of components #83

Closed paulhowardarm closed 3 years ago

paulhowardarm commented 3 years ago

Summary

Create a chapter in the book, which sets out the rules that must be followed to ensure that ongoing development is done in a backwards-compatible fashion from any given "stable" baseline release. These rules should enable the project to make a release that we can confidently deploy into long-term supported environments such as enterprise Linux distributions.

Background

Parsec is a long-running service that will be configured, deployed, upgraded and maintained over a long period of time. The environment will also have client applications that are calling it, and persistent state that is collected on storage. Components will be upgraded over time. For Parsec to be supportable in enterprise system, it must not be brittle against upgrades. If Parsec is upgraded and restarted, client applications should continue working, and all persistent state from prior versions should remain valid and usable.

What Is Needed?

Stability rules are primarily needed for the wire protocol and the service, since it is the Parsec service that will be packaged and deployed with Linux distributions. Client libraries will typically not be packaged as binaries, and will rather be consumed as source code dependencies, and their versioning systems will tend to be language-specific, with versioning rules (such as semantic versioning) being applied on a per-library basis. One grey area here is the parsec-tool, which, like the service, might be packaged as a binary, and might be upgraded separately from the service, while also being consumed by shell scripts on the system (which we don't want to break).

Ultimately, all of the rules will need to be enforced by CI tests so that PRs can automatically be checked, which would likely involve installing a baseline version, running a test suite, and then upgrading to the new version and running a regression suite of some kind. However, for now the scope is limited to capturing and documenting the rules. We can't enforce the rules until we have stated what they are.

Fortunately, much of Parsec has already been designed with backwards compatibility in mind, but some of these design choices need to be made explicit.

In no particular order, here's a non-exhaustive list of things that we may need to include. We can use comments on this issue to refine these and gather more ideas.

Client Libraries

As mentioned above, client libraries are subject to somewhat different rules because they typically would not be deployed as binary packages, hence API compatibility is more important than ABI. For the case of the Rust and Go clients, this is largely covered by semantic versioning, and it becomes an issue for the individual clients and the per-language ecosystems in which they operate. The parsec-book would not be expected to cover all of these, except perhaps with a few general notes.

One open question for the client side is to what extent they should be calling ListOpcodes to determine support for particular operations. As and when Parsec reaches some "first stable release" milestone, is that the point where we decide that the opcode set is fixed, and clients should never assume the existence of any newer opcodes without first querying dynamically?

ionut-arm commented 3 years ago

As was described (in short) in https://github.com/parallaxsecond/parsec-interface-rs/issues/36 , the ProviderID structure should get some attention when it comes to the stability of the client libraries. At the moment we treat these IDs as static - the same provider always has the same ID on every build. But this will change in the future where dynamic providers might be a thing or if we want to support multiple providers of the same type in one deployment. The main issue is that clients use ProviderID to identify these providers. A breaking change will happen at some point in how we handle them, so we can end up with clients that are trying to use "static" IDs while the service assigns them randomly.

hug-dev commented 3 years ago

I did a bit more research on this topic.

I propose to transform this issue into an investigation on how to reach and ensure stability of the Parsec service. As libraries, the Parsec clients are already following exisiting stability rules. The parsec-tool's stability can be investigated in a separate issue on that repo.

What does stability mean for the Parsec service?

I choose the following definition of stability for the Parsec service: whatever two versions A and B of the parsec binary, B being newer than A, A and B are stable if A can be removed and replaced by B without breaking anything.

Note that we are not looking at stability in the reverse direction: B can not be replaced by A without breaking anything.

Let's try to use the principle of semantic versioning to describe the stability of Parsec versions and when breaking changes are done. If parsec version is at 1.0.0 then all future stable version to that will be 1.x.y.

What needs to be stable?

With the above definition, Parsec stays stable if it keeps the exact same communication channels and format with its environment. Those are already described in the dataflow diagram in the threat model, reproduced below:

image

The communication channels that needs to be stable are (some of them are not in the diagram):

  1. Communication with clients
  2. Communication with Identity Providers
  3. Communication received from the CLI invocation
  4. Configuration file (including default configuration)
  5. Key mappings
  6. OS signals/systemd communication
  7. Dynamic libraries dependencies

Maybe I forgot some other dependencies on the environment. If we are to write a chapter on the book explaining Parsec stability, this list should be written there and during PArsec development we should look for new dependencies introducing stability concerns.

Each point in detail

Let's look at each of the points above and see how stability is ensured there, what are the future problems, how it can be tested.

1: clients

The wire protocol used for communicating requests/responses is stable by design and versioned. The open/close principle is used. One thing to look at is if the introduction of new parameters (for example new algorithm or new key type) constitutes a breaking change or not. For example PSA Crypto 1.0.1 introduced new alg and key type. In protobuf, we encode those things using oneof fields so we need to check if adding a new field is a breaking change or not.

For testing we need to check that the same set of tests work for different versions of Parsec.

2: identity providers

Or authenticators in general.

For testing we can check that different versions of Parsec work with the same authenticators (or different versions of authenticators).

3: CLI invocation

The CLI options used to invoke Parsec should remain stable. Currently there is only one parameter -c, --config to indicate the path to the configuration file.

We should test that this option is always available for future stable versions of Parsec.

4: Configuration file (and default)

The same configuration files should work for newer stable versions of Parsec. The default options in the configuration files should remain the same. If a new field/option is added in the configuration file, this field must be optional for stable versions.

To test, a set of different configuration files should be tested accross different Parsec versions to check that Parsec still spins up correctly.

5: Key mappings

Mappings between KeyTriple and KeyInfo are persistently stored somewhere. New stable Parsec versions should successfully load old mappings and store current ones (of the same KeyInfoManager).

For the OnDiskKeyInfoManager:

  1. base64 encoding of application names are used as directory names
  2. provider ID are used as directory names
  3. base64 encoding of key names are used as file names
  4. key ID type is serialised/deserialised by Serde
  5. the Attributes field directly come from the psa-crypto crate, (de)serialised by Serde

Stability:

  1. The application names should remain representable as UTF-8 strings. ListClients and DeleteClient are based on that. Stable.
  2. Might become a problem if provider ID switch from static numbers (1: MbedCrypto, 2: PKCS 11, 3: TPM, etc) to dynamically allocated IDs (allowing external providers to be dynamically loaded, without prior Parsec knowledge). Providers UUID could be a stable provider reference (but could the same provider be loaded twice? For example two PKCS 11 providers pointing at different PKCS 11 libraries).
  3. Stable as key names are UTF-8 strings.
  4. If the key ID type is not stable accross Parsec versions, then it won't be possible to load old mappings.
  5. If the Attributes structure is modified, then deserialisiation will fail for old mappings.

4 and 5. If Serde modifies the way it (de)serialise data, then old mappings won't be read as well.

parallaxsecond/parsec#271: if multiple authenticators are to be supported simultaneously, that will also probably impact the mappings, specially the OnDiskKeyInfoManager. The same application name should yield different keys under different authenticators.

Actions to take for those points and testing will be handled in another issue.

6: OS signals/systemd communication

Parsec currently answers to SIGTERM and SIGKILL. Those signals should still have the same behaviour on Parsec for future new versions. Parsec notifies systemd that it is ready/reloading/stopping using sd_notify. This behaviour should remain the same for future stable Parsec versions.

For testing, those signals and systemd integration should be exerced for different stable versions of Parsec.

7: Dynamic libraries dependencies

A new stable version of Parsec should not introduce a requirement on a new library dependency. The dynamic libraries dependencies should be listed (with their working versions). The only ones I can think of now are the TSS libraries.

Dynamically loaded libraries like PKCS 11 implementations should also be looked at.

For testing, different versions of Parsec should be checked with the same set of TSS (or others) libraries.

Actions to take