From ff85ea5170c92c759068fc2df02d3bd9c2e1fb36 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Tue, 1 Jun 2021 12:54:50 +0200 Subject: [PATCH 01/17] Release 0.9 (#523) --- Cargo.lock | 2 +- Cargo.toml | 2 +- Changelog.md | 235 +-- doc/openapi-pubd.yaml | 764 -------- doc/openapi.yaml | 2872 ---------------------------- src/commons/eventsourcing/store.rs | 4 +- 6 files changed, 43 insertions(+), 3836 deletions(-) delete mode 100644 doc/openapi-pubd.yaml delete mode 100644 doc/openapi.yaml diff --git a/Cargo.lock b/Cargo.lock index 35893eb6..c76e239a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,7 +1112,7 @@ dependencies = [ [[package]] name = "krill" -version = "0.9.0-rc3" +version = "0.9.0" dependencies = [ "base64 0.13.0", "basic-cookies", diff --git a/Cargo.toml b/Cargo.toml index 37ac2d19..6e984567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] # Note: some of these values are also used when building Debian packages below. name = "krill" -version = "0.9.0-rc3" +version = "0.9.0" edition = "2018" authors = [ "The NLnet Labs RPKI team " ] description = "Resource Public Key Infrastructure (RPKI) daemon" diff --git a/Changelog.md b/Changelog.md index 1192203c..ffdfb094 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,215 +1,56 @@ # Change Log -## 0.9.0 RC 3 -This release candidate reverses the proposed split in binaries for Krill CA and Publication Server -functions. Separate binaries were introduced in an effort to make things less confusing to end-users, -meaning we would not show functions that most users do not need. However, it seems that the benefits -to users who use Krill for CA functions only were marginal, at the expense of complicating operations -for Publication Server users significantly. +## 0.9.0 'One for All' -Updated documentation for this version is available here: -https://krill.docs.nlnetlabs.nl/en/v0.9.0rc3/ +This is the first major release of Krill in a while. -In addition to this a number of smaller issues were resolved as well: -- Optimistic rebuild in case of surplus events found (#530) -- Reschedule failures *after* the time to check again has passed, not before! (#524) -- The UI translations have now been updated (#381) +While basic ROA management is unchanged, there were many changes under the hood: -Unfortunately we could not find any volunteers to help us with the French translations of new strings -in the UI. The UI will fall back to English for these items. If you are a native French speaker and -would like to help out, please let us know. +- Multi-user support in the User Interface (local users or OpenID Connect) +- Reduce disk space usage and growth over time +- API and naming consistency (in preparation for 1.0 in future) +- Publication Server improvements (to whom it may concern) +- Many small improvements and minor bug fixes +For a full list of issues that were included in this release see: +https://github.com/NLnetLabs/krill/projects/4 -## 0.9.0 RC 2 +Updated documentation is available here: +https://krill.docs.nlnetlabs.nl/en/stable/index.html -This release candidate fixes a number of issues introduced in 0.9.0-rc1: +With multi-user support you can now give people in your organization individual access rights to +your CA - and they no longer need to share a password. If you have an OpenID Connect provider then +you can integrate Krill with it. Read more here: +https://krill.docs.nlnetlabs.nl/en/stable/multi-user.html -- Log migration progress and speed up process (#503) -- Rename auto-renewal commands in history (#501) -- Re-issue objects properly during a key rollover (#509) -- Withdraw objects when removing a parent (#508) +Krill versions before 0.9.0 keep a lot of data around that is not strictly needed. This can clog up +your system *and* it makes the Krill history difficult to parse. History can seen using `krillc history`. +We will include support for inspecting history in the UI soon. -Furthermore we made the following improvements: +There were some API and CLI changes introduced in this release. Over time things had become a bit +inconsistent and we felt we needed to fix that before we can consider going for the Krill 1.0 release. +If you are using automation then these changes may break your current integrations. Please have a +look at the following page to see if and how this affects you: +https://krill.docs.nlnetlabs.nl/en/stable/upgrade.html -- Report *which* file/dir was involved in case of I/O errors (#495) -- Change HTTP access log to 'debug'. Use KRILL_HTTP_LOG_INFO=1 if you want 'info' (#513) -- Refine logging command / change logging (#518) -- Improve certificate request logic and logging (#514) +Note that your Krill data store will be upgraded automatically if you upgrade to this release. This +upgrade can take some time, up to around 30 minutes dependent on the amount of history which accumulated +over time and the speed of your system. During the migration you will not be able to update your ROAs, +but your existing ROAs will remain available to RPKI validators. I.e. there is no downtime expected +with regards to RPKI validation. -Regarding certificate request logic and logging. Krill CAs will now report *which* new resources -were received from, or removed by a parent. As part of this change we also fixed a harmless, -but annoying, bug in certificate request logic. Krill would wrongfully report that a parent had -reduced the eligible 'not after' time, when in fact it had extended it, and then request the -new certificate regardless. Krill will now report correctly, and will only request a new certificate -if the new 'not after' time is more than 10% further into the future compared to the current certificate. -This is safe and will reduce noise levels where parent CAs use a simple strategy which returns a -new 'not after' time for every request. +We have tested this on various (big) Krill instances running CAs as well as Publication Servers. Still, +we recommend that you make a backup of your data store before upgrading. In case the upgrade should +unexpectedly fail for you, please restore your old data, run the previous binary, and contact us so +that we can make a fix. Alternatively, copy your data except for the `keys` directory to a test system +and then use the new Krill binary there with the following env variable set so you can test the data +migration: -The UI also received some fixes: -- Show the repository status properly (introduced in 0.9.0-rc1) -- Update the link to documentation -- Show the alert banner for new versions only for 'production' version + KRILL_UPGRADE_ONLY=1 -## 0.9.0 RC 1 - -This release introduces a number of breaking API changes as well as new functionality. We invite users -to test this release and contact us in case of any issues, comments or questions. - -A quick overview of the main points: - -- API - -If you are currently using the CLI and/or API for automation please check the updated documentation here: -https://krill.readthedocs.io/en/latest/cli.html - -- Data Migration - -This new release includes an automated migration and clean up of existing data. We have tested this -migration of course, but still we would very much like to encourage existing users to help us test -this further on real data. - -In order to test the upgrade you can do the following: -- install Krill 0.9.0-rc2 on a test machine -- copy over your existing data directory, but exclude the `keys` directory -- set the ENV variable KRILL_UPGRADE_ONLY -- start krill v0.9.0-rc1 using your copied data directory - -A minimal Krill config file to achieve this would be: -``` -data_dir = "/path/to/your/copied/data" -admin_token = "secret" -``` - -If all goes well you should see the message: -``` - Krill upgrade successful -``` - -Note, by excluding the `keys` directory you make sure that this new Krill instance cannot do anything -with your current private keys. E.g. it cannot publish anything or request certificates from your -parent CA. - -If there are any issues please let us know so that we can resolve these issues before we finalize the -Krill v0.9.0 release. If you would like us to test your data instead then please contact us. We will -then ask you to give us a tar ball of your data directory - again excluding the `keys` directory. - -- Publication Server - -The Krill Publication Server now uses its own binaries for the server and CLI. If you are using Krill -to host your own Publication Server, then please have a good look at the section below called -"Separate binary for Publication Server". Most importantly, be aware that the new binaries `krillpubd` -and `krillpubc` are not included in the debian packages or Docker images for the moment. We have -two open issue to resolve this before the 0.9.0 release (#361 and #490 respectively). For the moment -you will need to use `cargo` to install these binaries as described here: - -https://krill.readthedocs.io/en/latest/install-and-run.html - - -Please read below for more details! - - -### Breaking Changes - -This is release includes breaking changes in the Krill API. In addition to this we have also added -dedicated binaries for running your own Publication Server (`krillpubd`) and its own CLI (`krillpubc`). -You will also find that a number of commands of `krillc` have been changed or moved around. While -we take no pleasure in introducing such breaking changes to users we felt that they were necessary -for the future maintainability and useability of Krill. Furthermore, with these changes we believe -that we will be on track for a 1.0.0 release in the near future. - -If you were using the CLI or API for automation please have a look at the updated documentation here: -https://krill.readthedocs.io/en/latest/cli.html - - - -### Login with Named Users - -The Krill User Interface now has support for named users. Until version 0.8.2 Krill had -one admin token that was used by the CLI, API and UI. Now you can configure multiple users -using a local configuration file, or an OpenID Connect provider and have different access -restrictions for every user. The CLI still relies on a single admin token. - -You can read more about this new feature here: -https://krill.readthedocs.io/en/latest/multi-user.html - - -### Data Usage and History - -Krill uses an event sourced architecture, meaning that its current state is the direct result of -all past events that occurred. Rather than just persisting the current state itself, it can -be rebuilt based on those events. We believe that this is an important feature, because it -ensures by definition that the history, or audit log, of past events explains the current state -of Krill CAs. So, this is very attractive from a security and accountability perspective. - -Krill history until version 0.8.2 has been growing excessively because every RPKI CA has to -republish new Manifests and CRLs regularly as a protection against potential replay attacks. -Over time this results in a lot of changes, and a lot of history. - -However, the changes resulting from the republication of Manifests and CRLs are not really -interesting in the long term because they do not reflect any semantic changes resulting from -user actions, or interactions between CAs and their parent or child CAs. - -Therefore we decided to change the Krill architecture to a hybrid event-sourcing model. All -changes, except republication, are still tracked as commands and events in the Krill history. -So there is a complete overview of all semantically important changes maintained. - -As a result of this the amount disk space used by Krill is reduced significantly. When you -upgrade to Krill 0.9.0 your existing history will be migrated and cleaned up automatically. - - -### Master Token is now Admin Token - -See issue #433. Up until now Krill was using an array of inconsistent and inappropriate -names to refer to the token that can be used for authentication and authorization. We have -now renamed all these references to "ADMIN TOKEN". - -This means that the `auth_token` in the `krill.conf` file was renamed to `admin_token`, -The env variable alternative for the Krill daemon was renamed from `KRILL_AUTH_TOKEN` -to `KRILL_ADMIN_TOKEN`. - -In this release Krill will still support the old names, but we may remove this support -in a future release. - -### Separate binary for Publication Server - -Krill now has a dedicated binary for running an RPKI Publication Server, and a dedicated CLI -for managing it: `krillpubd` and `krillpubc`. - -We believe that having dedicated binaries for this purpose will make Krill easier to manage -for the vast majority of operators who do not need to run their own Publication Server. And -for operators who do need to run their own Publication Server, this will make it easier to -manage different access control settings for their Krill Publication and CA Servers, and it -will make it easier to migrate or phase out Publication Servers in future. - -#### Migration for existing users - -If you are using Krill to operate a CA only, and you use third party service for your RPKI -repository, then this change does not affect you. - -- Dedicated Mode - -If you are running a dedicated Krill instance to operate your own publication server, you should -start it using the newly provided binary 'krillpubd' instead of 'krill'. You can use the same -configuration file and data directory that you were using until now. - -- Mixed Mode - -If you are running Krill in a "mixed mode" where you operate both one or more CAs **and** you have -an embedded repository server then no action is required at this time. You can continue to use the -`krill` binary and your current configuration file and data directory. - -You will be required to run a separate `krill` binary for your CA(s) and `krillpubd` for your -Publication Server in a future release, when we have implemented this [open issue](https://github.com/NLnetLabs/krill/issues/480) -to allow existing CAs to migrate to using a new repository by doing a specialized key roll. - -### Other fixes - -ROAs will now be recreated if your CA lost a prefix, and gets it back later. This will help in -the rather unlikely case that a parent CA temporarily removed one of your resources in error. (#96) - -Let the Publication Server write the notification.xml file to a new file, and then rename it. -This prevents that Relying Parties can retrieve a half-written file. (#352) +Finally, note that you need to run at least Krill 0.6.0 in order to upgrade. If you run an older version +you will need to upgrade to version 0.8.2 first. ## 0.8.2 'Can't touch this' diff --git a/doc/openapi-pubd.yaml b/doc/openapi-pubd.yaml deleted file mode 100644 index 7dc503a6..00000000 --- a/doc/openapi-pubd.yaml +++ /dev/null @@ -1,764 +0,0 @@ -openapi: "3.0.2" -info: - title: Krill RPKI Publication Server API - version: 0.9.0-rc2 - description: | - # Introduction - - Many operators can use an RPKI Publication Server provided by - a third party (notably nic.br and APNIC), but others will need to - run their own. If you do, then please first read more about it - [here](https://rpki.readthedocs.io/en/latest/krill/publication-server.html). - - Note that you can manage a Krill Publication Server through the [Krill CLI](https://rpki.readthedocs.io/en/latest/krill/cli.html). However, if you need - to automate things, then using the API as described in this document may be more appropriate. - - ## Versioning - The API path includes a version. The idea is that we may add functionality, - but will not introduce breaking changes to existing functionality. You may - expect additional resources, and you may see additional data (JSON members) - within resources. So, please make sure that you ignore any JSON members that - you don’t support or need when using this API. - - ## References - Information about many of the concepts and data types that the Krill Publication - Server works with can be found in the following Internet standards documents: - - [RFC 8181: A Publication Protocol for the Resource Public Key Infrastructure (RPKI)](https://tools.ietf.org/html/rfc8181) - - [RFC 8183: An Out-of-Band Setup Protocol for Resource Public Key Infrastructure (RPKI) Production Services](https://tools.ietf.org/html/rfc8183) - - ## Feedback - Please give feedback and report issues concerning this API specification by - creating a [Krill GitHub issue](https://github.com/NLnetLabs/krill/issues/new). - - # Authentication - All Krill API endpoints are protected by [HTTP Bearer Authentication](https://swagger.io/docs/specification/authentication/bearer-authentication/). - To use the endpoints the client must supply an HTTP Authorization header of the form: - ``` - Authorization: Bearer - ``` - The token to use is defined in the Krill server configuration. Do not include - the '<>' brackets in the request. For more information consult the [Krill documentation](https://rpki.readthedocs.io/en/latest/krill/running.html#admin-token). - - # Errors - The format of and level of detail included in error reports from a Krill - server depend on the type of failure that occurred: - - **5XX/Server Errors:** This type of failure occurs within or is caused - by the environment in which Krill operates, or by bugs within Krill - itself. Examples include network issues, resource starvation, access - denied to resources, etc. Such errors are outside the control of Krill - and may result in HTTP 5XX errors from a proxy in front of Krill or from - Krill itself, or could manifest themselves as TCP timeouts and/or - connection failures. - - **4XX/Client Errors:** This type of failure occurs when the input - provided to Krill is incorrect, either due to user error, adherence by - the client to incorrect Krill API specifications, or malicious activity. - Krill responds with HTTP 4XX errors, where possible with a HTTP response - body in JSON format consisting of an error code and error message. - **Note:** Clients should decide which action to take based on the error - code, not on the error message. Such errors have the following form: - ``` - { - "label": "string", - "msg": "string", - "args": { - "key1": "string", - "key2": "string" - } - } - ``` - - Possible keys in the error response json are: - ``` - "cause": "The underlying error, usually a system error" - "publisher": "The handle of the publisher" - ``` - license: - name: Mozilla Public License 2.0 - url: https://www.mozilla.org/en-US/MPL/2.0/ - x-logo: - url: https://nlnetlabs.nl/static/logos/Krill/Krill_Logo_FC.svg - backgroundColor: #FFFFFF - -servers: - - url: https://{domain}:{port}/api/v1 - description: Krill server API URL. - variables: - domain: - default: localhost - port: - enum: - - '443' - - '3000' - default: '3000' - -tags: - - name: "Publishers" - description: | - Manage Certificate Authorities which are authorized to publish at your - Publication Server over the RFC8181 protocol. Such CAs are called - 'publishers' by the Krill Publication Server. - - name: "Pubd" - description: | - Initialize your Publication Server instance with your public repository URIs, - or delete your current instance so that it can be re-initialized. - - name: "Other" - -security: - - KRILL_ADMIN_TOKEN: [] - -paths: - /authorized: - get: - operationId: is_authorized - tags: - - "Other" - summary: Check if the client is authorized. - responses: - '200': - $ref: '#/components/responses/SuccessResponse' - '403': - $ref: '#/components/responses/ForbiddenResponse' - - /pubd/publishers: - get: - operationId: list_publishers - tags: - - "Publishers" - summary: List publishers. - responses: - '200': - $ref: '#/components/responses/ListPublishersResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: add_publisher - tags: - - "Publishers" - summary: Add publisher. - description: | - Adds an RFC 8181 publisher. Expects a JSON representation of the - [RFC 8183 XML](https://tools.ietf.org/html/rfc8183#section-5.2.3). - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AddPublisherRequestSchema' - responses: - '200': - $ref: '#/components/responses/AddPublisherResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubDuplicateSchema' - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /pubd/publishers/{publisher_handle}: - get: - operationId: get_publisher - tags: - - "Publishers" - summary: Get details for publisher. - description: Gets the information for a publisher by a given handle. - parameters: - - $ref: '#/components/parameters/publisher_handle' - responses: - '200': - $ref: '#/components/responses/GetPublisherResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '404': - $ref: '#/components/responses/ErrorPubUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - delete: - operationId: delete_publisher - tags: - - "Publishers" - summary: Delete a publisher. - description: This action also removes all the published content for a publisher. - parameters: - - $ref: '#/components/parameters/publisher_handle' - responses: - '200': - $ref: '#/components/responses/SuccessResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '404': - $ref: '#/components/responses/ErrorPubUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /pubd/publishers/{publisher_handle}/response.{format}: - get: - operationId: get_publisher_repository_response - tags: - - "Publishers" - summary: Get Repository Response for publisher. - description: | - Gets the [RFC 8183 Repository Response](https://tools.ietf.org/html/rfc8183#section-5.2.4) - for a publisher. - parameters: - - $ref: '#/components/parameters/publisher_handle' - - $ref: '#/components/parameters/format' - responses: - '200': - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8181Schema' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183RepositoryResponseXMLSchema' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '404': - $ref: '#/components/responses/ErrorPubUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /pubd/stale/{seconds}: - get: - operationId: list_publishers_stale_seconds - tags: - - "Publishers" - summary: List all publishers which have not published for X seconds. - description: | - Lists all publishers which have not published for the given time (seconds). Typically one - might expect updates from Krill publishers every 16 hours. - parameters: - - $ref: '#/components/parameters/seconds' - responses: - '200': - $ref: '#/components/responses/StalePublishersResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /pubd/init: - post: - operationId: initialise_pubd - tags: - - "Pubd" - summary: Initialise your Publication Server instance. - description: | - Initialises your Publication Server with the public RRDP and - rsync base URIs which reflect the setup of your public HTTPs - and rsync servers. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PubdInitSchema' - responses: - '200': - $ref: '#/components/responses/SuccessResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - - $ref: '#/components/schemas/ErrorPubdAlreadyInitialisedSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - delete: - operationId: delete_pubd - tags: - - "Pubd" - summary: Delete your Publication Server instance - description: | - Deletes the active instance of your Publication Server, - so that it can be initialised again with new URIs. Note - that this will fail if you have any publishers defined. - You should remove those first, and inform your users. - responses: - '200': - $ref: '#/components/responses/SuccessResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - $ref: '#/components/schemas/ErrorPubdHasPublishersSchema' - '403': - $ref: '#/components/responses/ForbiddenResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - -components: - securitySchemes: - KRILL_ADMIN_TOKEN: - type: http - scheme: bearer - bearerFormat: KRILL_ADMIN_TOKEN - - responses: - SuccessResponse: - description: Success. - - ForbiddenResponse: - description: Forbidden (incorrect authentication token). - - NotFoundResponse: - description: Not Found. - - GeneralErrorResponse: - description: General CA Server issue. - content: - application/json: - schema: - $ref: '#/components/schemas/GeneralErrorSchema' - - ErrorPubDuplicateResponse: - description: Publisher handle already in use - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubDuplicateSchema' - - ErrorPubUnknownResponse: - description: Unknown publisher - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubUnknownSchema' - - ErrorPubdNotEnabledResponse: - description: Publication Server not enabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubdNotEnabledSchema' - - ErrorPubdNotInitialisedResponse: - description: Publication Server not initialised - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubdNotInitialisedSchema' - - ErrorPubdAlreadyInitialisedResponse: - description: Publication Server already initialised - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubdAlreadyInitialisedSchema' - - ErrorPubdHasPublishersResponse: - description: Publication Server cannot be removed, still has publishers - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorPubdHasPublishersSchema' - - ListPublishersResponse: - description: Success. - content: - application/json: - schema: - type: object - properties: - publishers: - type: array - items: - type: object - properties: - handle: - $ref: '#/components/schemas/HandleSchema' - - example: - publishers: - - handle: 'ca' - - GetPublisherResponse: - description: Success. - content: - application/json: - schema: - type: object - properties: - handle: - $ref: '#/components/schemas/HandleSchema' - id_cert: - $ref: '#/components/schemas/IdCertSchema' - base_uri: - type: string - format: uri - current_files: - type: array - items: - type: object - properties: - base64: - type: string - format: base64 - uri: - type: string - format: uri - example: - handle: 'child' - id_cert: 'MIID..zw==' - base_uri: 'rsync:///repo/child/' - current_files: - - base64: 'MIIK..TZPZ' - uri: 'rsync:///repo/child/0/A815..A01C.mft' - - base64: 'MIIB..6Lg8' - uri: 'rsync:///repo/child/0/A815..A01C.crl' - - base64: 'MIIJ..Cg4=' - uri: 'rsync:///repo/child/0/3130..3936.roa' - - AddPublisherResponse: - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8181Schema' - - StalePublishersResponse: - description: Success. - content: - application/json: - schema: - type: object - properties: - publishers: - type: array - items: - type: object - properties: - handle: - $ref: '#/components/schemas/HandleSchema' - - example: - publishers: - - handle: 'ca' - - - schemas: - GeneralErrorSchema: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [general-error] - msg: - type: string - example: cause - args: - required: - - cause - properties: - cause: - type: string - example: cause - - ErrorPubDuplicateSchema: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [pub-duplicate] - msg: - type: string - example: Duplicate publisher 'ca' - args: - type: object - required: - - publisher - properties: - publisher: - type: string - example: ca - - ErrorPubUnknownSchema: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [pub-unknown] - msg: - type: string - example: Unknown publisher 'ca' - args: - required: - - publisher - properties: - publisher: - type: string - example: ca - - ErrorPubdNotEnabledSchema: - type: object - required: - - label - - msg - properties: - label: - type: string - enum: [pub-no-server] - msg: - type: string - example: Publication Server not enabled - - ErrorPubdNotInitialisedSchema: - type: object - required: - - label - - msg - properties: - label: - type: string - enum: [pub-repo-not-initialised] - msg: - type: string - example: Publication Server not initialised - - ErrorPubdAlreadyInitialisedSchema: - type: object - required: - - label - - msg - properties: - label: - type: string - enum: [pub-repo-initialised] - msg: - type: string - example: Publication Server already initialised - - ErrorPubdHasPublishersSchema: - type: object - required: - - label - - msg - properties: - label: - type: string - enum: [pub-repo-has-publishers] - msg: - type: string - example: Publication Server cannot be removed, still has publishers - - HandleSchema: - type: string - maxLength: 255 - pattern: '[\-_A-Za-z0-9/]*' - - IdCertSchema: - type: string - format: base64 - - RepoInfoSchema: - type: object - properties: - base_uri: - type: string - format: uri - rpki_notify: - type: string - format: uri - - ServiceUriSchema: - type: string - format: uri - - RfcCommonSchema: - type: object - properties: - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCertSchema' - - Rfc8181BaseSchema: - allOf: - - $ref: '#/components/schemas/RfcCommonSchema' - - type: object - properties: - publisher_handle: - $ref: '#/components/schemas/HandleSchema' - Rfc8181Schema: - allOf: - - $ref: '#/components/schemas/Rfc8181BaseSchema' - - type: object - properties: - service_uri: - $ref: '#/components/schemas/ServiceUriSchema' - repo_info: - $ref: '#/components/schemas/RepoInfoSchema' - - AddPublisherRequestSchema: - type: object - required: - - publisher_handle - - id_cert - properties: - publisher_handle: - $ref: '#/components/schemas/HandleSchema' - tag: - type: string - id_cert: - $ref: '#/components/schemas/IdCertSchema' - externalDocs: - url: https://tools.ietf.org/html/rfc8183#section-5.2.3 - - - - RFC8183RepositoryResponseXMLSchema: - type: object - properties: - xmlns: - type: string - format: url - xml: - attribute: true - version: - type: integer - xml: - attribute: true - publisher_handle: - type: string - xml: - attribute: true - service_uri: - type: string - format: uri - xml: - attribute: true - sia_base: - type: string - format: uri - xml: - attribute: true - rrdp_notification_uri: - type: string - format: uri - xml: - attribute: true - repository_bpki_ta: - type: string - format: base64 - xml: - name: 'repository_response' - # The example below is not shown by ReDoc, presumably due to: - # https://github.com/Redocly/redoc/issues/492 - example: - xmlns: http://www.hactrn.net/uris/rpki/rpki-setup/ - version: 1 - publisher_handle: 'ta' - service_uri: 'https://localhost:3000/rfc8181/ta' - sia_base: 'rsync://localhost/repo/ta/' - rrdp_notification_uri: 'https://localhost:3000/rrdp/notification.xml' - repository_bpki_ta: 'MIID..aQ==' - - PubdInitSchema: - type: object - required: - - rrdp_base_uri - - rsync_jail - properties: - rrdp_base_uri: - type: string - format: uri - example: https://example.com/rrdp/ - rsync_jail: - type: string - format: uri - example: rsync://example.com/repo/ - - - parameters: - format: - in: path - name: format - schema: - type: string - enum: ['json','xml'] - required: true - publisher_handle: - in: path - name: publisher_handle - schema: - $ref: '#/components/schemas/HandleSchema' - required: true - seconds: - in: path - name: seconds - schema: - type: integer - required: true \ No newline at end of file diff --git a/doc/openapi.yaml b/doc/openapi.yaml deleted file mode 100644 index 626395c5..00000000 --- a/doc/openapi.yaml +++ /dev/null @@ -1,2872 +0,0 @@ -openapi: "3.0.2" -info: - title: Krill RPKI Server API - version: 0.9.0-rc2 - description: | - # Introduction - Welcome to the documentation for the Krill server API, a JSON based - REST-like API for managing your Krill Certificate Authorities and - Publishers. For more information read the [Krill documentation](https://rpki.readthedocs.io/en/latest/krill/). - - ## Clients - To interact with a Krill server using this API, either: - - Use the [Krill CLI](https://rpki.readthedocs.io/en/latest/krill/cli.html). - - Load this API specification into the Swagger [editor](https://editor.swagger.io/) - or [viewer](https://petstore.swagger.io/) and use it to submit requests to - a Krill server via your browser. - - Use an [OpenAPI 3 generator](https://openapi-generator.tech/docs/generators.html) - to generate client code based on this API specification. - - Write your own client code that adheres to this specification. - - ## Versioning - The API path includes a version. The idea is that we may add functionality, - but will not introduce breaking changes to existing functionality. You may - expect additional resources, and you may see additional data (JSON members) - within resources. So, please make sure that you ignore any JSON members that - you don’t support or need when using this API. - - ## References - Information about many of the concepts and data types that Krill works with - can be found in the following Internet standards documents: - - [RFC 6492: A Protocol for Provisioning Resource Certificates](https://tools.ietf.org/html/rfc6492) - - [RFC 8181: A Publication Protocol for the Resource Public Key Infrastructure (RPKI)](https://tools.ietf.org/html/rfc8181) - - [RFC 8183: An Out-of-Band Setup Protocol for Resource Public Key Infrastructure (RPKI) Production Services](https://tools.ietf.org/html/rfc8183) - - ## Feedback - Please give feedback and report issues concerning this API specification by - creating a [Krill GitHub issue](https://github.com/NLnetLabs/krill/issues/new). - - # Authentication - All Krill API endpoints are protected by [HTTP Bearer Authentication](https://swagger.io/docs/specification/authentication/bearer-authentication/). - To use the endpoints the client must supply an HTTP Authorization header of the form: - ``` - Authorization: Bearer - ``` - The token to use is defined in the Krill server configuration. Do not include - the '<>' brackets in the request. For more information consult the [Krill documentation](https://rpki.readthedocs.io/en/latest/krill/running.html#admin-token). - - # Errors - The format of and level of detail included in error reports from a Krill - server depend on the type of failure that occured: - - **5XX/Server Errors:** This type of failure occurs within or is caused - by the environment in which Krill operates, or by bugs within Krill - itself. Examples include network issues, resource starvation, access - denied to resources, etc. Such errors are outside the control of Krill - and may result in HTTP 5XX errors from a proxy in front of Krill or from - Krill itself, or could manifest themselves as TCP timeouts and/or - connection failures. - - **4XX/Client Errors:** This type of failure occurs when the input - provided to Krill is incorrect, either due to user error, adherence by - the client to incorrect Krill API specifications, or malicious activity. - Krill responds with HTTP 4XX errors, where possible with a HTTP response - body in JSON format consisting of an error code and error message. - **Note:** Clients should decide which action to take based on the error - code, not on the error message. Such errors have the following form: - ``` - { - "label": "string", - "msg": "string", - "args": { - "key1": "string", - "key2": "string" - } - } - ``` - - Possible keys in the error response json are: - ``` - "cause": "The underlying error, usually a system error" - "ca": "The handle of the CA that encountered an error" - "parent": "The handle by which the CA knows a parent involved in an error" - "child": "The handle by which the CA knows its child involved in an error" - "class_name": "The name of the RFC 6492 Resource Class involved in an error" - "key_id": "A Key Identifier for a key involved in an error" - "prefix": "The IPv4 or IPv6 prefix in a ROA" - "asn": "The ASN in a ROA" - "max_length": "The max length (if present) for a ROA with issues" - ``` - license: - name: Mozilla Public License 2.0 - url: https://www.mozilla.org/en-US/MPL/2.0/ - x-logo: - url: https://nlnetlabs.nl/static/logos/Krill/Krill_Logo_FC.svg - backgroundColor: #FFFFFF - -servers: - - url: https://{domain}:{port}/api/v1 - description: Krill server API URL. - variables: - domain: - default: localhost - port: - enum: - - '443' - - '3000' - default: '3000' - -tags: - - name: "Certificate Authorities" - - name: "Key Rolls" - description: | - Krill CAs can have 0 or more 'Resource Classes' under each of its (0 or - more) parents. Krill CAs will talk to all of their parents regularly and - ask them about their resource entitlements. Typically a CA will have one - parent and receive all their resources in a single resource class, but it's - also possible that resources are split into multiple resource classes. - Krill CAs can also have multiple parents and have resource classes under - each of them. - - When a krill CA first gets a resource class it will: - - create a pending key pair - - request a certificate - - The 'key state' of this resource class is then shown as type "Pending". - - When the CA receives a certificate for this new pair it will be promoted to - "Active", and publishing of objects will start. I.e. the CA will start - publishing a manifest and CRL, and any configured ROAs for which the - prefixes are held by this class. - - Resource classes in the state 'Active' can be rolled. The following phases - exist in a key roll: - - RollPending - - RollNew - - RollOld - - name: "Route Authorizations" - description: | - Managing Route Authorizations (ROA configs) - - name: "Other" - -security: - - KRILL_ADMIN_TOKEN: [] - -paths: - /authorized: - get: - operationId: is_authorized - tags: - - "Other" - summary: Check if the client is authorized. - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - - /cas: - get: - operationId: list_cas - tags: - - "Certificate Authorities" - summary: List CAs. - responses: - '200': - $ref: '#/components/responses/ListCAs' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: add_ca - tags: - - "Certificate Authorities" - summary: Add a CA. - description: | - When adding a CA you need to choose a “handle”, essentially just a - name. The term “handle” comes from RFC 8183 and is used in the - communication protocol between CAs and CAs and publication servers. - - When a CA has been added, it is registered to publish locally in the - Krill instance where it exists, but other than that it has no - configuration yet. In order to do anything useful with a CA you will - first have to add at least one parent to it, and then most likely some - Route Authorizations and/or Child CAs. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AddCARequest' - responses: - '200': - $ref: '#/components/responses/Success' - '400': - $ref: '#/components/responses/CaDuplicateResponse' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}: - get: - operationId: get_ca - tags: - - "Certificate Authorities" - summary: Get CA. - description: Get CA details. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/GetCA' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/id/child_request.{format}: - get: - operationId: get_ca_child_request - tags: - - "Certificate Authorities" - summary: Get CA RFC 8183 Request. - description: Gets the RFC 8181 Child Request for this CA as JSON or XML. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/format' - responses: - '200': - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183RequestXML' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/children: - post: - operationId: add_child_ca - tags: - - "Certificate Authorities" - summary: Add a child CA. - description: | - This adds a child CA. - parameters: - - $ref: '#/components/parameters/ca_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AddCAChildRequest' - examples: - child: - value: - handle: 'ca' - resources: - asn: 'AS1' - v4: '10.0.0.0/8' - v6: '::' - child_request: - tag: null - child_handle: 'ca' - id_cert: '' - responses: - '200': - $ref: '#/components/responses/Rfc8183ParentResponse' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildDuplicateResponse' - - $ref: '#/components/schemas/CaChildResourcesRequiredResponse' - - $ref: '#/components/schemas/CaChildResourcesExtraResponse' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/children/{child_handle}: - get: - operationId: get_child_ca - tags: - - "Certificate Authorities" - summary: Get child details. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/child_handle' - responses: - '200': - $ref: '#/components/responses/GetChildCA' - '403': - $ref: '#/components/responses/Forbidden' - '404': - description: Unknown resource. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildUnknownResponse' - - $ref: '#/components/schemas/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: update_child_ca - tags: - - "Certificate Authorities" - summary: Update an existing child of a CA. - description: | - This action is idempotent. - **Note:** You can only update the complete resource set! If you leave - out a resource - like asn in the example - then the child will lose all - resources of that type. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/child_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCAChildRequest' - examples: - update_resources: - summary: Update the resources for a child. - value: - id_cert: null - resources: - asn: '' - v4: '10.0.0.0/8' - v6: '::' - update_id_cert: - summary: Update the ID certificate for a remote child. - value: - id_cert: '' - resources: null - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '404': - description: Unknown resource. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildUnknownResponse' - - $ref: '#/components/schemas/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - delete: - operationId: delete_child_ca - tags: - - "Certificate Authorities" - summary: Remove a child CA. - description: | - Removes a child, revokes its certificates and removes them from the - repository. Use with care, although you can re-add a removed child, - this is probably best avoided. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/child_handle' - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '404': - description: Unknown resource. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildUnknownResponse' - - $ref: '#/components/schemas/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/children/{child_handle}/contact: - get: - operationId: get_child_ca_parent_contact - tags: - - "Certificate Authorities" - summary: Get parent contact for a child. - description: | - This is the parent contact that a child can use when adding the parent. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/child_handle' - responses: - '200': - $ref: '#/components/responses/Rfc8183ParentResponse' - '403': - $ref: '#/components/responses/Forbidden' - '404': - description: Unknown resource. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildUnknownResponse' - - $ref: '#/components/schemas/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/children/{child_handle}/parent_response.{format}: - get: - operationId: get_child_ca_parent_response - tags: - - "Certificate Authorities" - summary: Get the RFC 8183 Parent Response for a child. - description: | - To get the RFC 8183 Parent Response for a child. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/child_handle' - - $ref: '#/components/parameters/format' - responses: - '200': - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183ParentResponse' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183ParentResponseXML' - '403': - $ref: '#/components/responses/Forbidden' - '404': - description: Unknown resource. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaChildUnknownResponse' - - $ref: '#/components/schemas/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/id: - post: - operationId: regenerate_ca_id_cert - tags: - - "Certificate Authorities" - summary: Regenerate CA ID certificate. - description: | - Tells the CA to create a new ID certificate and forget its old ID. Note - that if you do this, you will have to create new RFC 8181 Child Request - and Publisher Request XML for the parent CA(s) and Repository. Don't - use this if you can avoid it. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/parents: - post: - operationId: add_ca_parent - tags: - - "Certificate Authorities" - summary: Add or update a parent to a CA. - description: | - Add a parent to the CA. Note that your CA will only request a certificate - from any of its parents after a repository has been configured for it. - - Submit the RFC 8183 Parent Response XML, or JSON. The JSON format expects - the local name for the parent as 'handle', whereas in case of XML this will - be taken from the `parent_handle`. - parameters: - - $ref: '#/components/parameters/ca_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AddParentCARequest' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183ParentResponseXML' - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaParentDuplicateResponse' - - $ref: '#/components/schemas/CaParentIssueResponse' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/parents/{parent_handle}: - get: - operationId: get_ca_parent_contact - tags: - - "Certificate Authorities" - summary: Get parent contact for a CA. - description: | - Get the parent contact information that a CA has for a parent. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/parent_handle' - responses: - '200': - $ref: '#/components/responses/Rfc8183ParentContact' - '403': - $ref: '#/components/responses/Forbidden' - '400': - $ref: '#/components/responses/CaParentUnknownResponse' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: update_ca_parent_contact - tags: - - "Certificate Authorities" - summary: Add or update a parent to a CA. - description: | - In this case the local name of the parent is specified in the path. An error - will be returned in case this name does not match the handle in the submitted JSON. - If XML was submitted then this will simply override the name found for `publisher_handle`. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/parent_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183ParentContact' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183ParentResponseXML' - responses: - '200': - $ref: '#/components/responses/Success' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaParentUnknownResponse' - - $ref: '#/components/schemas/CaParentIssueResponse' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - delete: - operationId: delete_ca_parent - tags: - - "Certificate Authorities" - summary: Remove a parent. - description: | - Remove a parent from a CA. - **Note:** this is just the child removing the parent, and this does not - remove the child from the parent. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/parent_handle' - responses: - '200': - $ref: '#/components/responses/Success' - '400': - $ref: '#/components/responses/CaParentUnknownResponse' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/repo: - get: - operationId: get_ca_repository - tags: - - "Certificate Authorities" - summary: Get Repository for a CA. - description: | - Returns details about the repository. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/GetRepositoryForCA' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: update_ca_repository - tags: - - "Certificate Authorities" - summary: Update the Repository for a CA. - description: | - This will add a repository to the CA, if one was not defined earlier. - In that case it will also trigger that the CA requests resource certificates - from its parents (if configured) now that it knows which URIs to use. - - If the CA did have a repository configured, then this will trigger an - update of the configured repository by means of a key rollover for all - existing certificates received under each parent. The new key will use - the new repository, but the current key will keep using the old repository - until the new key is activated. Note API end point for key activiation - will need to be called for this (this will activate *ALL* keys for the CA). - - Post either an RFC 8183 Repository Response XML file, or a JSON equivalent. - parameters: - - $ref: '#/components/parameters/ca_handle' - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - repository_response: - $ref: '#/components/schemas/Rfc8183RepositoryResponse' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183RepositoryResponseXML' - responses: - '200': - $ref: '#/components/responses/Success' - '400': - description: Bad request parameters. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CaRepoSameResponse' - - $ref: '#/components/schemas/CaRepoIssueResponse' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/id/publisher_request.{format}: - get: - operationId: get_ca_publisher_request - tags: - - "Certificate Authorities" - summary: Get the RFC 8183 Publisher Request for a CA. - parameters: - - $ref: '#/components/parameters/ca_handle' - - $ref: '#/components/parameters/format' - responses: - '200': - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183PublisherRequest' - application/xml: - schema: - $ref: '#/components/schemas/RFC8183PublisherRequestXML' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/keys/roll_init: - post: - operationId: init_key_roll - tags: - - "Key Rolls" - summary: Initiate a key roll. - description: | - When a key roll is initiated the following steps are executed: - - A new key pair is created in state 'pending'. - - A resource certificate is requested for the new key. - - The resource class will now have state 'RollPending' - - However, you may not observe this for long, as the request is sent to - the parent immediately, and as soon as a certificate is received for - the new key, the state moves from 'RollPending' to 'RollNew' and a MFT - and CRL are published for the new key. All other objects (ROAs, - delegated certificates) are still published under the active key. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/keys/roll_activate: - post: - operationId: activate_key_roll - tags: - - "Key Rolls" - summary: Finish a key roll. - description: | - According the RFC new keys can be activated only after 24 hours. In - practice however, this can also be done sooner. The main thing to keep - in mind is that is only done after the parent CA has *published* the - new certificate for the 'new' key - which might be some time after you - actually received the certificate. - - In short: it's best to perform this step 24 hours after initialising a - key roll, but if your old key is really compromised then you should - probably just go ahead and do this asap. - - When a key roll is finished, the following happens: - - The state is moved from 'RollNew' to 'RollOld'. - - The new key becomes 'active' and will now also publish ROAs and - delegates certs. - - The former 'active' key becomes 'old' and only publishes a manifest - and CRL, but no other objects. - - A revocation request for the 'old' key is created. - - This state is also easy to miss, because Krill will immediately (but - asynchronously) send the revocation request to the applicable parent - and when it receives confirmation that the key is revoked it will: - - Withdraw the manifest and crl for that key. - - Move the state to 'Active' (and forget the old key). - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /bulk/cas/publish: - post: - operationId: republish_all_cas - tags: - - "Certificate Authorities" - summary: Force that all CAs create new objects if needed (in which case they will also sync). - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - - /bulk/cas/sync/parent: - post: - operationId: refresh_all_cas - tags: - - "Certificate Authorities" - summary: Force that all CAs ask their parents for updated certificates. - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - - /bulk/cas/sync/repo: - post: - operationId: resync_all_cas - tags: - - "Certificate Authorities" - summary: Force that all CAs sync with their repo server. - responses: - '200': - $ref: '#/components/responses/Success' - '403': - $ref: '#/components/responses/Forbidden' - - /cas/{ca_handle}/routes: - get: - operationId: list_route_authorizations - tags: - - "Route Authorizations" - summary: List route authorizations. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - description: Success. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ROA' - example: - - asn: 64497 - prefix: 192.168.1.0/24 - max_length: 16 - - asn: 64497 - prefix: 10.0.3.0/24 - max_length: 24 - - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - post: - operationId: update_route_authorizations - tags: - - "Route Authorizations" - summary: Update route authorizations. - description: | - Updates the ROA configurations. Users of a CA can configure which - routes they want to be authorized, and Krill will then take care of - creating the actual ROA RPKI objects. - - Updates are sent as a delta containing all additions and removals. This - is done because often it is needed to update multiple ROAs in a single - transaction in order to avoid invalidating certain announcements. E.g. - when you have announcements from more than one ASN, or when you also - announce more specific prefixes. - parameters: - - $ref: '#/components/parameters/ca_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ROADelta' - example: - added: - - asn: 64497 - prefix: 192.168.1.0/24 - max_length: 16 - - asn: 64497 - prefix: 10.0.3.0/24 - max_length: 24 - removed: - - asn: 64497 - prefix: 10.0.0.0/24 - max_length: 24 - - responses: - '200': - $ref: '#/components/responses/Success' - '400': - $ref: '#/components/responses/RoaDeltaError' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/routes/try: - post: - operationId: try_update_route_authorizations - tags: - - "Route Authorizations" - summary: Try to update route authorizations, returns advice in case of issues - description: | - Tries to updates the ROA configurations. - - Returns an empty response if successful, or an extended advice in case the - update would result in any invalid announcements for the prefixes in the - change set. - parameters: - - $ref: '#/components/parameters/ca_handle' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ROADelta' - example: - added: - - asn: 64497 - prefix: 192.168.1.0/24 - max_length: 16 - - asn: 64497 - prefix: 10.0.3.0/24 - max_length: 24 - removed: - - asn: 64497 - prefix: 10.0.0.0/24 - max_length: 24 - - responses: - '200': - description: Try update response. - content: - application/json: - schema: - oneOf: - - $ref: '#/components/responses/Success' - - $ref: '#/components/schemas/BgpAnalysisAdvice' - example: - effect: - - asn: 64497 - prefix: 192.168.1.0/24 - state: roa_seen - authorizes: - - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64497 - prefix: 10.0.3.0/24 - state: roa_unseen - - asn: 64498 - prefix: 192.168.1.0/24 - state: roa_unseen - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - state: roa_too_permissive - authorizes: - - asn: 64496 - prefix: 10.0.0.0/22 - - asn: 64496 - prefix: 10.0.2.0/23 - disallows: - - asn: 64497 - prefix: 10.0.0.0/22 - - asn: 64496 - prefix: 10.0.0.0/24 - - asn: 0 - prefix: 10.0.4.0/24 - state: roa_as0 - - asn: 0 - prefix: 192.168.1.0/24 - state: roa_as0_redundant - made_redundant_by: - - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64498 - prefix: 192.168.1.0/24 - - asn: 64496 - prefix: 10.0.0.0/22 - state: announcement_valid - allowed_by: - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64496 - prefix: 10.0.2.0/23 - state: announcement_valid - allowed_by: - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 192.168.1.0/24 - state: announcement_valid - allowed_by: - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64496 - prefix: 10.0.0.0/24 - state: announcement_invalid_length - disallowed_by: - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 10.0.0.0/22 - state: announcement_invalid_asn - disallowed_by: - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 10.0.0.0/21 - state: announcement_not_found - - asn: 64496 - prefix: 192.168.0.0/24 - state: announcement_not_found - - asn: 64497 - prefix: 192.168.0.0/24 - state: announcement_not_found - suggestion: - stale: - - asn: 64497 - prefix: 10.0.3.0/24 - - asn: 64498 - prefix: 192.168.1.0/24 - not_found: - - asn: 64497 - prefix: 10.0.0.0/21 - - asn: 64496 - prefix: 192.168.0.0/24 - - asn: 64497 - prefix: 192.168.0.0/24 - invalid_asn: - - asn: 64497 - prefix: 10.0.0.0/22 - invalid_length: - - asn: 64496 - prefix: 10.0.0.0/24 - too_permissive: - - current: - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - new: - - asn: 64496 - prefix: 10.0.0.0/22 - - asn: 64496 - prefix: 10.0.2.0/23 - as0_redundant: - - asn: 0 - prefix: 192.168.1.0/24 - keep: - - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 0 - prefix: 10.0.4.0/24 - - - '400': - $ref: '#/components/responses/RoaDeltaError' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/CaUnknownResponse' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - - /cas/{ca_handle}/routes/analysis/full: - get: - operationId: bgp_analysis_full - tags: - - "Route Authorizations" - summary: Show full analysis of all ROAs and Announcments for CA resources - description: | - Show all known announcements for the CA's resources and their RPKI validity - state compared to the CA's ROAs, as well as all the CA's ROAs and how they - affect the validatity state of known announcements. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/BgpAnalysisReport' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - - /cas/{ca_handle}/issues: - get: - operationId: show_ca_issues - tags: - - "Certificate Authorities" - summary: Show issues for a CA. - description: | - Issues currently include reachability or authentication - issues between the CA and its repository and/or parents. This may be - extended with other issues in future. - parameters: - - $ref: '#/components/parameters/ca_handle' - responses: - '200': - $ref: '#/components/responses/CertAuthIssues' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/GeneralErrorResponse' - -externalDocs: - description: Read the Krill documentation - url: https://rpki.readthedocs.io/en/latest/krill/ - -components: - securitySchemes: - KRILL_ADMIN_TOKEN: - type: http - scheme: bearer - bearerFormat: KRILL_ADMIN_TOKEN - - parameters: - ca_handle: - in: path - name: ca_handle - schema: - $ref: '#/components/schemas/Handle' - required: true - child_handle: - in: path - name: child_handle - schema: - $ref: '#/components/schemas/Handle' - required: true - parent_handle: - in: path - name: parent_handle - schema: - $ref: '#/components/schemas/Handle' - required: true - publisher_handle: - in: path - name: publisher_handle - schema: - $ref: '#/components/schemas/Handle' - required: true - format: - in: path - name: format - schema: - type: string - enum: ['json','xml'] - required: true - seconds: - in: path - name: seconds - schema: - type: integer - required: true - - schemas: - # Types ordered fundamental to complex - Handle: - type: string - maxLength: 255 - pattern: '[\-_A-Za-z0-9/]*' - IdCert: - type: string - format: base64 - PemIdCert: - type: object - nullable: true - properties: - pem: - type: string - format: pem - hash: - type: string - format: hash - RepoInfo: - type: object - properties: - base_uri: - type: string - format: uri - rpki_notify: - type: string - format: uri - - Resources: - type: object - properties: - asn: - type: string - v4: - type: string - v6: - type: string - example: - asn: "" - v4: "10.0.0.0/0" - v6: "2001:db8::/32" - - ROA: - type: object - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - - ROADelta: - type: object - properties: - added: - type: array - items: - $ref: '#/components/schemas/ROA' - removed: - type: array - items: - $ref: '#/components/schemas/ROA' - - ServiceUri: - type: string - format: uri - - rfcCommon: - type: object - properties: - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCert' - - Rfc8183ParentContact: - type: object - properties: - type: - type: string - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCert' - parent_handle: - $ref: '#/components/schemas/Handle' - child_handle: - $ref: '#/components/schemas/Handle' - service_uri: - $ref: '#/components/schemas/ServiceUri' - example: - type: "rfc6492" - tag: null - id_cert: "MIIDNDCCAhygAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyg5NjczMEUwQTg2MkE0RDkyNjQ1NEY5RTgzNzYxMzMzQkI0Qjc0QTVDMB4XDTIxMDMwNDE2MTQwNFoXDTM2MDMwNDE2MTkwNFowMzExMC8GA1UEAxMoOTY3MzBFMEE4NjJBNEQ5MjY0NTRGOUU4Mzc2MTMzM0JCNEI3NEE1QzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOCNekDo59PLdJnfiPg9kUycEb3EF17TnnUEKJaCunheH/brWakMumqpBox3H2fn1XY7e2e1SFezp52yqcIggLUOj3K49SvTzdDCCwA2MGuzaDHK6IhOuLKH9D9BqA2FTWVr3PNk77g+Bn1TfKF+G+JMr3jvDMkJAW9+58vnl7UJ/g+H6/lWqWAW1JeA3go9B+qdxC9DA02h+7vXPGhsVeUw688LFBv1fDGlQFX02zx0uNrKQQHddL1aJIM01i5M+N3uWJ5u3wCJRvxAr/P9KNtCO1sTFxR2dE/8W0+rfJWkAnDcgzeDCjznICF1146Thjxir08dYlj5YAeR3c5r5L8CAwEAAaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlnMOCoYqTZJkVPnoN2EzO7S3SlwwHwYDVR0jBBgwFoAUlnMOCoYqTZJkVPnoN2EzO7S3SlwwDQYJKoZIhvcNAQELBQADggEBAGUq4w7nv6Ez4kBdqfgyU0taKHgxIOTKJ6EwsDZj6eVGF6ThrutAonMVUeu1zs5ZW/uYaRWPffHzY8m1wdyrwYVw0HxRWY96vrhOOrVk32J6ip9V9bfFSjZLVnVcBz5V/odB++bJHhMoFNUaoqEGSNleiPotBtOHahlIL1EDEAt7bC5Kk7vEl0VmRJs7Hp1kpdZRJlVy7sLWL082hCJCulG57qL2UbsQ2wmFk+ImJ2RO3GSrNEI4//kHIw7GQFeeROfeb5HvyC/QOCzfBmMc/ipApuyzROSXHsE6CiaM2uWCjHs1NZBu+Za2EoFOhgYN1akeqqo50vRfO3Dd6Bm/rQY=" - parent_handle: "ta" - child_handle: "testbed" - service_uri: "https://localhost:3000/rfc6492/ta" - - # TODO: this looks identical to the Rfc8183ParentContact type immediately above? consolidate this? - Rfc8183ParentResponse: - type: object - properties: - type: - type: string - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCert' - parent_handle: - $ref: '#/components/schemas/Handle' - child_handle: - $ref: '#/components/schemas/Handle' - service_uri: - $ref: '#/components/schemas/ServiceUri' - example: - type: "rfc6492" - tag: null - id_cert: "MIIDNDCCAhygAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyg5NjczMEUwQTg2MkE0RDkyNjQ1NEY5RTgzNzYxMzMzQkI0Qjc0QTVDMB4XDTIxMDMwNDE2MTQwNFoXDTM2MDMwNDE2MTkwNFowMzExMC8GA1UEAxMoOTY3MzBFMEE4NjJBNEQ5MjY0NTRGOUU4Mzc2MTMzM0JCNEI3NEE1QzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOCNekDo59PLdJnfiPg9kUycEb3EF17TnnUEKJaCunheH/brWakMumqpBox3H2fn1XY7e2e1SFezp52yqcIggLUOj3K49SvTzdDCCwA2MGuzaDHK6IhOuLKH9D9BqA2FTWVr3PNk77g+Bn1TfKF+G+JMr3jvDMkJAW9+58vnl7UJ/g+H6/lWqWAW1JeA3go9B+qdxC9DA02h+7vXPGhsVeUw688LFBv1fDGlQFX02zx0uNrKQQHddL1aJIM01i5M+N3uWJ5u3wCJRvxAr/P9KNtCO1sTFxR2dE/8W0+rfJWkAnDcgzeDCjznICF1146Thjxir08dYlj5YAeR3c5r5L8CAwEAAaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlnMOCoYqTZJkVPnoN2EzO7S3SlwwHwYDVR0jBBgwFoAUlnMOCoYqTZJkVPnoN2EzO7S3SlwwDQYJKoZIhvcNAQELBQADggEBAGUq4w7nv6Ez4kBdqfgyU0taKHgxIOTKJ6EwsDZj6eVGF6ThrutAonMVUeu1zs5ZW/uYaRWPffHzY8m1wdyrwYVw0HxRWY96vrhOOrVk32J6ip9V9bfFSjZLVnVcBz5V/odB++bJHhMoFNUaoqEGSNleiPotBtOHahlIL1EDEAt7bC5Kk7vEl0VmRJs7Hp1kpdZRJlVy7sLWL082hCJCulG57qL2UbsQ2wmFk+ImJ2RO3GSrNEI4//kHIw7GQFeeROfeb5HvyC/QOCzfBmMc/ipApuyzROSXHsE6CiaM2uWCjHs1NZBu+Za2EoFOhgYN1akeqqo50vRfO3Dd6Bm/rQY=" - parent_handle: "ta" - child_handle: "testbed" - service_uri: "https://localhost:3000/rfc6492/ta" - - Rfc8183RepositoryResponse: - type: object - properties: - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCert' - publisher_handle: - $ref: '#/components/schemas/Handle' - service_uri: - $ref: '#/components/schemas/ServiceUri' - repo_info: - $ref: '#/components/schemas/RepoInfo' - example: - tag: null - publisher_handle: "publisher" - id_cert: 'MIID..6g==' - service_uri: "https://repo.example.com/rfc8181/publisher/" - repo_info: - base_uri: 'rsync://localhost/repo/ca/' - rpki_notify: 'https://localhost:3000/rrdp/notification.xml' - - Rfc8183PublisherRequest: - type: object - properties: - tag: - type: string - nullable: true - id_cert: - $ref: '#/components/schemas/IdCert' - publisher_handle: - $ref: '#/components/schemas/Handle' - example: - tag: null - publisher_handle: "publisher" - id_cert: 'MIID..6g==' - - - - Rfc8183: - allOf: - - $ref: '#/components/schemas/rfcCommon' - - type: object - properties: - child_handle: - $ref: '#/components/schemas/Handle' - - RFC8183PublisherRequestXML: - type: object - properties: - xmlns: - type: string - format: url - xml: - attribute: true - version: - type: integer - xml: - attribute: true - publisher_handle: - type: string - xml: - attribute: true - publisher_bpki_ta: - type: string - format: base64 - xml: - name: 'publisher_request' - example: - xmlns: http://www.hactrn.net/uris/rpki/rpki-setup/ - version: 1 - publisher_handle: 'child' - publisher_bpki_ta: 'MIID..6g==' - - RFC8183RepositoryResponseXML: - type: object - properties: - xmlns: - type: string - format: url - xml: - attribute: true - version: - type: integer - xml: - attribute: true - service_uri: - type: string - format: uri - xml: - attribute: true - publisher_handle: - type: string - xml: - attribute: true - sia_base: - type: string - xml: - attribute: true - rrdp_notification_uri: - type: string - xml: - attribute: true - repository_bpki_ta: - type: string - format: base64 - xml: - name: 'repository_response' - example: - xmlns: http://www.hactrn.net/uris/rpki/rpki-setup/ - version: 1 - service_uri: 'https://localhost:3000/rfc6492/ta' - publisher_handle: 'child' - sia_base: 'rsync://localhost/repo/ca/' - rrdp_notification_uri: 'https://localhost:3000/rrdp/notification.xml' - repository_bpki_ta: 'MIID..6g==' - - RFC8183RequestXML: - type: object - properties: - xmlns: - type: string - format: url - xml: - attribute: true - version: - type: integer - xml: - attribute: true - child_handle: - type: string - xml: - attribute: true - child_bpki_ta: - type: string - format: base64 - xml: - name: 'child_request' - example: - xmlns: http://www.hactrn.net/uris/rpki/rpki-setup/ - version: 1 - child_handle: 'child' - child_bpki_ta: 'MIID..6g==' - - RFC8183ParentResponseXML: - type: object - properties: - xmlns: - type: string - format: url - xml: - attribute: true - version: - type: integer - xml: - attribute: true - service_uri: - type: string - format: uri - xml: - attribute: true - child_handle: - type: string - xml: - attribute: true - parent_handle: - type: string - xml: - attribute: true - parent_bpki_ta: - type: string - format: base64 - xml: - name: 'parent_response' - example: - xmlns: http://www.hactrn.net/uris/rpki/rpki-setup/ - version: 1 - service_uri: 'https://localhost:3000/rfc6492/ta' - child_handle: 'child' - parent_handle: 'ta' - parent_bpki_ta: 'MIID..6g==' - - PendingKey: - type: object - properties: - key_id: - type: string - - CertifiedKey: - allOf: - - $ref: '#/components/schemas/PendingKey' - - type: object - properties: - incoming_cert: - type: object - properties: - cert: - type: string - format: base64 - uri: - type: string - format: uri - resources: - $ref: '#/components/schemas/Resources' - request: - type: string - nullable: true - - # Request bodies - AddCARequest: - type: object - required: - - handle - properties: - handle: - allOf: - - $ref: '#/components/schemas/Handle' - - description: The handle attribute uniquely identifies the new Certificate Authority within this Krill instance. - - AddCAChildRequest: - type: object - required: - - handle - - resources - - id_cert - properties: - handle: - allOf: - - $ref: '#/components/schemas/Handle' - - description: The handle attribute uniquely identifies the new Certificate Authority within this Krill instance. - resources: - $ref: '#/components/schemas/Resources' - id_cert: - type: string - format: base64 - - - UpdateCAChildRequest: - type: object - properties: - id_cert: - allOf: - - $ref: '#/components/schemas/IdCert' - - description: | - Base64 of DER encoded is PEM encoded minus the BEGIN and END - markers. - - nullable: true - resources: - allOf: - - $ref: '#/components/schemas/Resources' - - nullable: true - AddParentCARequest: - type: object - required: - - handle - - contact - properties: - handle: - allOf: - - $ref: '#/components/schemas/Handle' - - description: The local handle by which your ca refers to this parent. - contact: - $ref: '#/components/schemas/Rfc8183ParentContact' - - # Error codes ordered ascending by error code - CaRepoSameResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-repo-same] - msg: - type: string - example: CA 'ca' already using this repository - args: - required: - - ca - properties: - ca: - type: string - example: ca - CaRepoIssueResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-repo-issue] - msg: - type: string - example: CA 'ca' got error from repository 'cannot connect' - args: - required: - - ca - - cause - properties: - ca: - type: string - example: ca - cause: - type: string - example: cannot connect - CaChildDuplicateResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-child-duplicate] - msg: - type: string - example: CA 'ca' already has a child named 'child' - args: - required: - - ca - - child - properties: - ca: - type: string - example: ca - child: - type: string - example: child - - - CaChildUnknownResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-child-unknown] - msg: - type: string - example: CA 'ca' does not have a child named 'child' - args: - required: - - ca - - child - properties: - ca: - type: string - example: ca - child: - type: string - example: child - - CaChildResourcesRequiredResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-child-resources-required] - msg: - type: string - example: Child 'child' for CA 'ca' MUST have resources specified - args: - required: - - ca - - child - properties: - ca: - type: string - example: ca - child: - type: string - example: child - - CaChildResourcesExtraResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-child-resources-extra] - msg: - type: string - example: Child cannot have resources not held by parent. - args: - required: - - ca - - child - properties: - ca: - type: string - example: ca - child: - type: string - example: child - - CaParentDuplicateResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-parent-duplicate] - msg: - type: string - example: CA 'ca' already has a parent named 'parent' - args: - required: - - ca - - parent - properties: - ca: - type: string - example: ca - parent: - type: string - example: parent - - CaParentUnknownResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-parent-unknown] - msg: - type: string - example: CA 'ca' does not have a parent named 'parent' - args: - required: - - ca - - parent - properties: - ca: - type: string - example: ca - parent: - type: string - example: parent - - CaParentIssueResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-parent-issue] - msg: - type: string - example: CA 'ca' got error from parent 'parent' cause - args: - required: - - ca - - parent - - cause - properties: - ca: - type: string - example: ca - parent: - type: string - example: parent - cause: - type: string - example: cause - - BgpAnalysisAdvice: - type: object - required: - - effect - - suggestion - properties: - effect: - $ref: '#/components/schemas/BgpAnalysisReport' - suggestion: - $ref: '#/components/schemas/BgpAnalysisSuggestion' - - BgpAnalysisSuggestion: - type: object - required: - - stale - - too_permissive - - as0_redundant - - keep - - not_found - - invalid_asn - - invalid_length - - keep_disallowing - properties: - stale: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - - too_permissive: - type: array - items: - type: object - required: - - current - - new - properties: - current: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - new: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - - as0_redundant: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - - keep: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - - not_found: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - - invalid_asn: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - - invalid_length: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - - keep_disallowing: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - - BgpAnalysisReport: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - - state - - allowed_by - - disallowed_by - - made_redundant_by - - authorizes - - disallows - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - state: - type: string - allowed_by: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - disallowed_by: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - made_redundant_by: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - nullable: true - allows: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - disallows: - type: array - items: - type: object - required: - - asn - - prefix - properties: - asn: - type: integer - prefix: - type: string - - RoaDeltaErrorResponse: - type: object - required: - - label - - msg - - delta_error - properties: - label: - type: string - enum: [ca-roa-delta-error] - msg: - type: string - example: Delta rejected, see included json - delta_error: - $ref: '#/components/schemas/RoaDeltaErrorSchema' - - RoaDeltaErrorSchema: - type: object - required: - - duplicates - - covered - - notheld - - unknowns - - invalid_length - - covering - - as0_exists - - as0_overlaps - properties: - duplicates: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - covered: - type: array - items: - type: object - required: - - addition - - covered_by - properties: - addition: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - covered_by: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_length: - type: integer - - notheld: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - unknowns: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - invalid_length: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - covering: - type: array - items: - type: object - required: - - addition - - covering - properties: - addition: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - covering: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - as0_exists: - type: array - items: - type: object - required: - - addition - - existing_as0 - properties: - addition: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - existing_as0: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - as0_overlaps: - type: array - items: - type: object - required: - - addition - - existing - properties: - addition: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - existing: - type: array - items: - type: object - required: - - asn - - prefix - - max_length - properties: - asn: - type: integer - prefix: - type: string - max_lenght: - type: integer - - - CaDuplicateResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-duplicate] - msg: - type: string - example: CA 'ca' was already initialised - args: - required: - - ca - properties: - ca: - type: string - example: ca - - CaUnknownResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [ca-unknown] - msg: - type: string - example: CA 'ca' is unknown - args: - required: - - ca - properties: - ca: - type: string - example: ca - - GeneralErrorResponse: - type: object - required: - - label - - msg - - args - properties: - label: - type: string - enum: [general-error] - msg: - type: string - example: cause - args: - required: - - cause - properties: - cause: - type: string - example: cause - - responses: - Success: - description: Success. - - Forbidden: - description: Forbidden (incorrect authentication token). - - NotFound: - description: Not Found. - - CertAuthIssues: - description: Issues for a CA (e.g. cannot reach parent) - content: - application/json: - schema: - type: object - properties: - repo: - type: string - parents: - type: array - items: - type: string - additionalProperties: - type: string - example: - repo: "repostory unreachable" - parents: - "rir": "denied!" - "nir": "cannot connect" - - CaRepoSameResponse: - description: Same Repository - content: - application/json: - schema: - $ref: '#/components/schemas/CaRepoSameResponse' - - CaRepoIssueResponse: - description: Issue from repository for CA - content: - application/json: - schema: - $ref: '#/components/schemas/CaRepoIssueResponse' - - CaChildDuplicateResponse: - description: Duplicate child - content: - application/json: - schema: - $ref: '#/components/schemas/CaChildDuplicateResponse' - - CaChildUnknownResponse: - description: Unknown child - content: - application/json: - schema: - $ref: '#/components/schemas/CaChildUnknownResponse' - - CaChildResourcesRequiredResponse: - description: Child requires resources - content: - application/json: - schema: - $ref: '#/components/schemas/CaChildResourcesRequiredResponse' - - CaChildResourcesExtraResponse: - description: Child resources not held by CA - content: - application/json: - schema: - $ref: '#/components/schemas/CaChildResourcesExtraResponse' - - CaParentDuplicateResponse: - description: Duplicate parent - content: - application/json: - schema: - $ref: '#/components/schemas/CaParentDuplicateResponse' - - CaParentUnknownResponse: - description: Unknown parent - content: - application/json: - schema: - $ref: '#/components/schemas/CaParentUnknownResponse' - - CaParentIssueResponse: - description: Issue communicating with parent - content: - application/json: - schema: - $ref: '#/components/schemas/CaParentIssueResponse' - - RoaDeltaError: - description: Error in submitted ROA delta - content: - application/json: - schema: - $ref: '#/components/schemas/RoaDeltaErrorResponse' - example: - label: ca-roa-delta-error - msg: 'Delta rejected, see included json' - args: {} - delta_error: - duplicates: - - asn: 1 - prefix: 10.0.0.0/20 - max_length: 24 - covered: - - addition: - asn: 1 - prefix: 10.0.0.0/24 - covered_by: - asn: 1 - prefix: 10.0.0.0/20 - max_length: 24 - notheld: - - asn: 1 - prefix: 10.128.0.0/9 - unknowns: - - asn: 1 - prefix: 192.168.0.0/16 - invalid_length: - - asn: 1 - prefix: 10.0.1.0/25 - covering: - - addition: - asn: 1 - prefix: 10.0.0.0/16 - max_length: 24 - covering: - - asn: 1 - prefix: 10.0.0.0/20 - max_length: 24 - - asn: 1 - prefix: 10.0.128.0/24 - as0_exists: - - addition: - asn: 1 - prefix: 10.1.0.0/24 - existing_as0: - asn: 0 - prefix: 10.1.0.0/24 - as0_overlaps: - - addition: - asn: 0 - prefix: 10.2.0.0/16 - existing: - - asn: 1 - prefix: 10.2.0.0/24 - - asn: 1 - prefix: 10.2.1.0/24 - - - BgpAnalysisReport: - description: | - Full ROAs vs known BGP Announcements report for this CA. - - This is an array of both ROAs and Announcements, including all optional - information that may be applicable to either: - - - - - - - - -
elementapplies tomeaning
allowed_byannouncementList of ROAs authorizing this announcement
disallowed_byannouncementList of ROAs disallowing this announcement, see state for main reason
made_redundant_byroaAS0 ROA for prefix which is authorised for a real ASN
authorizesroaList of announcements authorized by this ROA
disallowsroaList of announcements disallowed by this ROA
- - Furthermore the schema includes max_length. This value will not be present for announcements. Confusing as this might be, this allows for parsing all kinds of states in a single table. - - Possible state values are: - - - - - - - - - - - - -
statemeaning
roa_seenThe ROA covers BGP announcements
roa_unseenThe ROA covers no BGP announcements
roa_too_permissiveThe ROA authorizes additional announcements
roa_as0AS0 ROA which may disallow announcements
announcement_validBGP announcement authorized by a ROA
announcement_invalid_lengthBGP announcement too specific for the ASN
announcement_invalid_asnBGP announcement prefix only allowed for othes ASNs
announcement_not_foundBGP announcement not covered by ROAs
roa_no_announcement_infoROA exists, but no BGP info available for analysis
- - - content: - application/json: - schema: - $ref: '#/components/schemas/BgpAnalysisReport' - example: - - asn: 64497 - prefix: 192.168.1.0/24 - state: roa_seen - authorizes: - - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64497 - prefix: 10.0.3.0/24 - state: roa_unseen - - asn: 64498 - prefix: 192.168.1.0/24 - state: roa_unseen - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - state: roa_too_permissive - authorizes: - - asn: 64496 - prefix: 10.0.0.0/22 - - asn: 64496 - prefix: 10.0.2.0/23 - disallows: - - asn: 64497 - prefix: 10.0.0.0/22 - - asn: 64496 - prefix: 10.0.0.0/24 - - asn: 0 - prefix: 10.0.4.0/24 - state: roa_as0 - - asn: 0 - prefix: 192.168.1.0/24 - state: roa_as0_redundant - made_redundant_by: - - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64498 - prefix: 192.168.1.0/24 - - asn: 64496 - prefix: 10.0.0.0/22 - state: announcement_valid - allowed_by: - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64496 - prefix: 10.0.2.0/23 - state: announcement_valid - allowed_by: - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 192.168.1.0/24 - state: announcement_valid - allowed_by: - asn: 64497 - prefix: 192.168.1.0/24 - - asn: 64496 - prefix: 10.0.0.0/24 - state: announcement_invalid_length - disallowed_by: - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 10.0.0.0/22 - state: announcement_invalid_asn - disallowed_by: - - asn: 64496 - prefix: 10.0.0.0/22 - max_length: 23 - - asn: 64497 - prefix: 10.0.0.0/21 - state: announcement_not_found - - asn: 64496 - prefix: 192.168.0.0/24 - state: announcement_not_found - - asn: 64497 - prefix: 192.168.0.0/24 - state: announcement_not_found - - - CaDuplicateResponse: - description: CA handle already in use. - content: - application/json: - schema: - $ref: '#/components/schemas/CaDuplicateResponse' - - CaUnknownResponse: - description: Unknown CA - content: - application/json: - schema: - $ref: '#/components/schemas/CaUnknownResponse' - - GeneralErrorResponse: - description: General CA Server issue. - content: - application/json: - schema: - $ref: '#/components/schemas/GeneralErrorResponse' - - GetCA: - description: Success. - content: - application/json: - schema: - type: object - properties: - handle: - $ref: '#/components/schemas/Handle' - id_cert: - $ref: '#/components/schemas/PemIdCert' - repo_info: - $ref: '#/components/schemas/RepoInfo' - parents: - type: array - items: - type: object - properties: - handle: - $ref: '#/components/schemas/Handle' - kind: - type: string - resources: - $ref: '#/components/schemas/Resources' - resources_classes: - type: object - additionalProperties: - x-additionalPropertiesName: resource index - type: object - properties: - name_space: - type: string - parent_handle: - $ref: '#/components/schemas/Handle' - keys: - oneOf: - - type: object - title: pending - properties: - pending: - type: object - properties: - pending_key: - $ref: '#/components/schemas/PendingKey' - - type: object - title: active - properties: - active: - type: object - properties: - active_key: - $ref: '#/components/schemas/CertifiedKey' - - type: object - title: roll_pending - properties: - roll_pending: - type: object - properties: - active_key: - $ref: '#/components/schemas/CertifiedKey' - pending_key: - $ref: '#/components/schemas/CertifiedKey' - - type: object - title: roll_new - properties: - roll_new: - type: object - properties: - new_key: - $ref: '#/components/schemas/CertifiedKey' - active_key: - $ref: '#/components/schemas/CertifiedKey' - - type: object - title: roll_old - properties: - roll_old: - type: object - properties: - old_key: - $ref: '#/components/schemas/CertifiedKey' - active_key: - $ref: '#/components/schemas/CertifiedKey' - current_objects: - type: object - additionalProperties: - x-additionalPropertiesName: object name - type: object - properties: - content: - type: string - format: base64 - serial: - type: string - expires: - type: string - format: date-time - children: - type: array - items: - type: string - example: - handle: 'ca' - repo_info: - base_uri: 'rsync://localhost/repo/ca/' - rpki_notify: 'https://localhost:3000/rrdp/notification.xml' - parents: - - handle: 'ta' - kind: 'ta' - resources: - asn: 'AS1-AS10' - v4: '10.0.0.0/8' - v6: '::/128' - resource_classes: - '0': - name_space: '0' - parent_handle: 'ta' - keys: - active: - active_key: - key_id: 'B738..8820' - incoming_cert: - cert: '' - uri: 'rsync://localhost/repo/ta/0/B738..8820.cer' - resources: - asn: 'AS0-AS10' - v4: '10.0.0.0/8' - v6: '::/128' - request: null - current_objects: - '2DCE..5337.crl': - content: '' - serial: '2' - expires: '2019-11-27T10:56:16.166449Z' - '2DCE..5337.mft': - content: '' - serial: '87065729750653489815092730763761996670858255271' - expires: '2019-11-27T10:56:16.172248Z' - children: - - 'child' - - ListCAs: - description: Success. - content: - application/json: - schema: - type: object - properties: - cas: - type: array - items: - type: object - properties: - handle: - allOf: - - $ref: '#/components/schemas/Handle' - - description: The RFC-8183 handle that uniquely identifies the CA in this Krill instance. - - GetChildCA: - description: Success. - content: - application/json: - schema: - type: object - properties: - id_cert: - $ref: '#/components/schemas/PemIdCert' - entitled_resources: - $ref: '#/components/schemas/Resources' - - - - GetRepositoryForCA: - description: Success. - content: - application/json: - schema: - type: object - properties: - contact: - type: object - properties: - repository_response: - $ref: '#/components/schemas/Rfc8183RepositoryResponse' - examples: - contact: - value: - contact: - repository_response: - tag: null - publisher_handle: 'ca' - id_cert: 'MIID..Vg==' - service_uri: 'https://localhost:3000/rfc8181/ca' - repo_info: - base_uri: 'rsync://localhost/repo/ca/' - rpki_notify: 'https://localhost:3000/rrdp/notificati.xml' - - GetCAHistory: - description: Success. - content: - application/json: - schema: - type: object - properties: - init: - type: object - properties: - id: - $ref: '#/components/schemas/Handle' - version: - type: integer - details: - type: object - events: - type: array - items: - type: object - properties: - id: - $ref: '#/components/schemas/Handle' - version: - type: integer - details: - type: object - - Rfc8183ParentResponse: - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183ParentResponse' - - Rfc8183ParentContact: - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183ParentContact' - - Rfc8183O: - description: Success. - content: - application/json: - schema: - $ref: '#/components/schemas/Rfc8183' - diff --git a/src/commons/eventsourcing/store.rs b/src/commons/eventsourcing/store.rs index 380d4a7e..ba8ff475 100644 --- a/src/commons/eventsourcing/store.rs +++ b/src/commons/eventsourcing/store.rs @@ -49,6 +49,7 @@ impl Default for StoredValueInfo { #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] // Do NOT EVER change the order.. this is used to check whether migrations are needed +// See this issue for planned fix: https://github.com/NLnetLabs/krill/issues/521 #[allow(non_camel_case_types)] pub enum KeyStoreVersion { Pre0_6, @@ -60,11 +61,12 @@ pub enum KeyStoreVersion { V0_8_1, V0_8_2, V0_9_0_RC1, + V0_9_0, } impl KeyStoreVersion { pub fn current() -> Self { - KeyStoreVersion::V0_9_0_RC1 + KeyStoreVersion::V0_9_0 } } From ce4bbf874242af8d1e4090e000fbc7bc55fb43e1 Mon Sep 17 00:00:00 2001 From: Rich Compton Date: Wed, 23 Jun 2021 05:46:17 -0600 Subject: [PATCH 02/17] Mention up-down protocol in krill.conf (#585) --- defaults/krill.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/defaults/krill.conf b/defaults/krill.conf index 46e044ec..b2d4af4c 100644 --- a/defaults/krill.conf +++ b/defaults/krill.conf @@ -111,7 +111,7 @@ # CA certificate refresh rate # # This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# CA via the up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # @@ -265,4 +265,4 @@ # timing_child_certificate_valid_weeks = 52 # timing_child_certificate_reissue_weeks_before = 4 # timing_roa_valid_weeks = 52 -# timing_roa_reissue_weeks_before = 4 \ No newline at end of file +# timing_roa_reissue_weeks_before = 4 From fb31fc39e48b2a6ae980cd17719c832e43f5b524 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Wed, 23 Jun 2021 14:50:24 +0200 Subject: [PATCH 03/17] Improve comment in krill.conf and fix related tests. --- defaults/krill.conf | 6 +++--- test-resources/krill-init-multi-user.conf | 4 ++-- test-resources/krill-init.conf | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/defaults/krill.conf b/defaults/krill.conf index b2d4af4c..07552904 100644 --- a/defaults/krill.conf +++ b/defaults/krill.conf @@ -110,8 +110,8 @@ # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA via the up-down protocol and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # @@ -265,4 +265,4 @@ # timing_child_certificate_valid_weeks = 52 # timing_child_certificate_reissue_weeks_before = 4 # timing_roa_valid_weeks = 52 -# timing_roa_reissue_weeks_before = 4 +# timing_roa_reissue_weeks_before = 4 \ No newline at end of file diff --git a/test-resources/krill-init-multi-user.conf b/test-resources/krill-init-multi-user.conf index eca51c4d..c5e5bec1 100644 --- a/test-resources/krill-init-multi-user.conf +++ b/test-resources/krill-init-multi-user.conf @@ -110,8 +110,8 @@ service_uri = "https://localhost:3001/" # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # diff --git a/test-resources/krill-init.conf b/test-resources/krill-init.conf index c1575679..a809dd4e 100644 --- a/test-resources/krill-init.conf +++ b/test-resources/krill-init.conf @@ -110,8 +110,8 @@ service_uri = "https://localhost:3001/" # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # From eca1eb061d00d83bfc775a9221b690c77e6e7895 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Tue, 29 Jun 2021 10:48:34 +0200 Subject: [PATCH 04/17] RPM packaging (#536) --- .github/workflows/pkg.yml | 397 ++++++++++++------ .rpm/krill.spec | 32 -- Cargo.toml | 46 +- .../krill-debian-bullseye.krill.service | 0 .../common}/krill-debian-buster.krill.service | 0 .../krill-debian-stretch.krill.service | 0 .../common}/krill-ubuntu-bionic.krill.service | 0 .../common}/krill-ubuntu-focal.krill.service | 0 .../common}/krill-ubuntu-xenial.krill.service | 0 {debian => pkg/common}/krill.service.preset | 0 {debian => pkg/debian}/description.txt | 0 {debian => pkg/debian}/postinst | 0 {debian => pkg/debian}/postrm | 0 {debian => pkg/debian}/preinst | 0 pkg/rpm/postinst | 66 +++ pkg/rpm/postrm | 9 + 16 files changed, 383 insertions(+), 167 deletions(-) delete mode 100644 .rpm/krill.spec rename {debian => pkg/common}/krill-debian-bullseye.krill.service (100%) rename {debian => pkg/common}/krill-debian-buster.krill.service (100%) rename {debian => pkg/common}/krill-debian-stretch.krill.service (100%) rename {debian => pkg/common}/krill-ubuntu-bionic.krill.service (100%) rename {debian => pkg/common}/krill-ubuntu-focal.krill.service (100%) rename {debian => pkg/common}/krill-ubuntu-xenial.krill.service (100%) rename {debian => pkg/common}/krill.service.preset (100%) rename {debian => pkg/debian}/description.txt (100%) rename {debian => pkg/debian}/postinst (100%) rename {debian => pkg/debian}/postrm (100%) rename {debian => pkg/debian}/preinst (100%) create mode 100755 pkg/rpm/postinst create mode 100644 pkg/rpm/postrm diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 31f73328..e95ed69b 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -1,19 +1,15 @@ -# GitHub Actions workflow for building and testing Krill O/S packages. -# Uses GitHub Actions caching to avoid rebuilding Rust cargo-deb and -# Krill dependencies on every run. +# GitHub Actions workflow for building and testing Krill O/S packages. Uses GitHub Actions caching to avoid rebuilding +# Rust cargo-deb , cargo generate-rpm and Krill compiled dependencies on every run. # -# Note: at the time of writing the GH cache contents expire after a -# week if not used so the next build may be much slower as it will -# have to re-download/build/install lots of Rust crates. +# Note: at the time of writing the GH cache contents expire after a week if not used so the next build may be much +# slower as it will have to re-download/build/install lots of Rust crates. # -# Packages are built inside Docker containers as GH Runners have extra libraries -# and packages installed which can cause package building to succeed but package -# installation on a real target O/S to fail, due to being built against too +# Packages are built inside Docker containers as GH Runners have extra libraries and packages installed which can cause +# package building to succeed but package installation on a real target O/S to fail, due to being built against too # recent version of a package such as libssl or glibc. # -# Packages are tested inside LXC/LXD containers because Docker containers don't -# by default support init managers such as systemd but we want to test systemd -# service unit installation and activation. +# Packages are tested inside LXC/LXD containers because Docker containers don't by default support init managers such as +# systemd but we want to test systemd service unit installation and activation. name: Packaging on: @@ -51,9 +47,12 @@ defaults: shell: bash --noprofile --norc -eo pipefail -x {0} jobs: - # Use the cargo-deb Rust crate to build a Debian package for installing - # Krill. See: https://github.com/mmstick/cargo-deb - deb-pkg: + # Use the cargo-deb and cargo-generate-rpm Rust crates to build Debian and RPM packages respectively for installing + # Krill. + # See: + # - https://github.com/mmstick/cargo-deb + # - https://github.com/cat-in-136/cargo-generate-rpm + pkg: strategy: matrix: image: # can't use complex values here, only primitive values are allowed @@ -63,16 +62,21 @@ jobs: - 'debian:stretch' # debian/9 - 'debian:buster' # debian/10 - 'debian:bullseye' # debian/11 + - 'centos:7' + - 'centos:8' + include: + - image: 'centos:7' + extra_build_args: '--features static-openssl' env: CARGO_DEB_VER: 1.28.0 - # A Krill version of the form 'x.y.z-plus' denotes a dev build that is - # newer than the released x.y.z version but is not yet a new release. + CARGO_GENERATE_RPM_VER: 0.4.0 + # A Krill version of the form 'x.y.z-bis' denotes a dev build that is newer than the released x.y.z version but is + # not yet a new release. NEXT_VER_LABEL: bis - name: deb-pkg + name: pkg runs-on: ubuntu-latest - # Build on the oldest platform we are targeting in order to avoid - # https://github.com/rust-lang/rust/issues/57497. Specifying container - # causes all of the steps in this job to run inside a Docker container. + # Build on the oldest platform we are targeting in order to avoid https://github.com/rust-lang/rust/issues/57497. + # Specifying container causes all of the steps in this job to run inside a Docker container. container: ${{ matrix.image }} steps: @@ -80,9 +84,8 @@ jobs: id: setvars shell: bash run: | - # Get the operating system and release name (e.g. ubuntu and xenial) from - # the image name (e.g. ubuntu:xenial) by extracting only the parts before - # and after but not including the colon: + # Get the operating system and release name (e.g. ubuntu and xenial) from the image name (e.g. ubuntu:xenial) by + # extracting only the parts before and after but not including the colon: echo "OS_NAME=${MATRIX_IMAGE%:*}" >> $GITHUB_ENV echo "OS_REL=${MATRIX_IMAGE#*:}" >> $GITHUB_ENV env: @@ -92,12 +95,19 @@ jobs: - name: Checkout repository uses: actions/checkout@v1 - # Install Rust the hard way rather than using a GH Action because the action - # doesn't work inside a Docker container. + # Install Rust the hard way rather than using a GH Action because the action doesn't work inside a Docker container. - name: Install Rust run: | - apt-get update - apt-get install -y curl + case ${OS_NAME} in + debian|ubuntu) + apt-get update + apt-get install -y curl + ;; + centos) + yum update -y + ;; + esac + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --profile minimal -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH env: @@ -105,7 +115,17 @@ jobs: - name: Install compilation and other dependencies run: | - apt-get install -y build-essential jq libssl-dev lintian pkg-config + case ${OS_NAME} in + debian|ubuntu) + apt-get install -y build-essential jq libssl-dev lintian pkg-config + ;; + centos) + yum install epel-release -y + yum update -y + yum install -y jq openssl-devel rpmlint + yum groupinstall -y "Development Tools" + ;; + esac env: DEBIAN_FRONTEND: noninteractive @@ -117,33 +137,56 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git - target key: ${{ matrix.image }}-cargo-${{ hashFiles('**/Cargo.lock') }} - # Speed up cargo-deb installation by only re-downloading and re-building its - # dependent crates if we change the version of cargo-deb that we are using. - - name: Cache Cargo Deb binary + # Speed up tooling installation by only re-downloading and re-building dependent crates if we change the version of + # the tool that we are using. + - name: Cache Cargo Deb if available id: cache-cargo-deb uses: actions/cache@v2 with: path: ~/.cargo/bin/cargo-deb key: ${{ matrix.image }}-cargo-deb-${{ env.CARGO_DEB_VER }} - # Only install cargo-deb if not already fetched from the cache. - - name: Install Cargo Deb + - name: Cache Cargo Generate RPM if available + id: cache-cargo-generate-rpm + uses: actions/cache@v2 + with: + path: ~/.cargo/bin/cargo-generate-rpm + key: ${{ matrix.image }}-cargo-generate-rpm-${{ env.CARGO_GENERATE_RPM_VER }} + + # Only install cargo-deb or cargo-generate-rpm if not already fetched from the cache. + - name: Install Cargo Deb if needed if: steps.cache-cargo-deb.outputs.cache-hit != 'true' run: | - cargo install cargo-deb --version $CARGO_DEB_VER + case ${OS_NAME} in + debian|ubuntu) + cargo install cargo-deb --version ${CARGO_DEB_VER} + ;; + esac - # Instruct cargo-deb to build the Debian package using the config section - # in Cargo.toml for the specified "variant". - - name: Create the DEB package + - name: Install Cargo Generate RPM if needed + if: steps.cache-cargo-generate-rpm.outputs.cache-hit != 'true' run: | - # Packages for different distributions (e.g. Stretch, Buster) of the same - # O/S (e.g. Debian) when served from a single package repository MUST have - # unique package_ver_architecture triples. Cargo deb can vary the name based - # on the 'variant' config section in use, but doesn't do so according to - # Debian policy (as it modifies the package name, not the package version). + case ${OS_NAME} in + centos) + cargo install cargo-generate-rpm --version ${CARGO_GENERATE_RPM_VER} + ;; + esac + + # Instruct cargo-deb or cargo-generate-rpm to build the package based on Cargo.toml settings and command line + # arguments. + - name: Create the package + env: + MATRIX_IMAGE: ${{ matrix.image }} + EXTRA_BUILD_ARGS: ${{ matrix.extra_build_args }} + run: | + # Debian + # ============================================================================================================== + # Packages for different distributions (e.g. Stretch, Buster) of the same O/S (e.g. Debian) when served from a + # single package repository MUST have unique package_ver_architecture triples. Cargo deb can vary the name based + # on the 'variant' config section in use, but doesn't do so according to Debian policy (as it modifies the + # package name, not the package version). # Format: package_ver_architecture # Where ver has format: [epoch:]upstream_version[-debian_revision] # And debian_version should be of the form: 1 @@ -151,68 +194,115 @@ jobs: # See: # - https://unix.stackexchange.com/a/190899 # - https://www.debian.org/doc/debian-policy/ch-controlfields.html#version - # - https://readme.phys.ethz.ch/documentation/debian_version_numbers/ # Therefore we generate the version ourselves. # - # In addition, Semantic Versioning and Debian version policy cannot - # express a pre-release label in the same way. For example 0.8.0-rc.1 - # is a valid Cargo.toml [package].version value but when used as a - # Debian package version 0.8.0-rc.1 would be considered _NEWER_ than - # the final 0.8.0 release. To express this in a Debian compatible way we - # must replace the dash '-' with a tilda '~'. + # In addition, Semantic Versioning and Debian version policy cannot express a pre-release label in the same way. + # For example 0.8.0-rc.1 is a valid Cargo.toml [package].version value but when used as a Debian package version + # 0.8.0-rc.1 would be considered _NEWER_ than the final 0.8.0 release. To express this in a Debian compatible + # way we must replace the dash '-' with a tilda '~'. # - # Finally, sometimes we want a version to be NEWER than the latest - # release but without having to decide what higher semver number to bump - # to. In this case we do NOT want dash '-' to become '~' because `-` - # is treated as higher and tilda is treated as lower. - KRILL_VER=$(cargo read-manifest | jq -r '.version' | tr '-' '~') - DEB_KRILL_VER=$(echo $KRILL_VER | sed -e "s/~$NEXT_VER_LABEL/-$NEXT_VER_LABEL/") + # RPM + # ============================================================================================================== + # Handle the release candidate case where the version string needs to have dash replaced by tilda. The cargo + # build command won't work if the version key in Cargo.toml contains a tilda but we have to put the tilda there + # for when we run cargo generate-rpm so that it uses it. + # + # For background on RPM versioning see: + # https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/ + # + # COMMON + # ============================================================================================================== + # Finally, sometimes we want a version to be NEWER than the latest release but without having to decide what + # higher semver number to bump to. In this case we do NOT want dash '-' to become '~' because `-` is treated as + # higher and tilda is treated as lower. + KRILL_VER=$(cargo read-manifest | jq -r '.version') + KRILL_NEW_VER=$(echo $KRILL_VER | tr '-' '~') + PKG_KRILL_VER=$(echo $KRILL_NEW_VER | sed -e "s/~$NEXT_VER_LABEL/-$NEXT_VER_LABEL/") - case ${{ github.event_name }} in - pull_request) MAINTAINER="${{ github.actor }} " ;; - push) MAINTAINER="${{ github.event.pusher.name }} <${{ github.event.pusher.email }}>" ;; - *) echo 2>&1 "ERROR: Unexpected GitHub Actions event"; exit 1 ;; + case ${OS_NAME} in + debian|ubuntu) + case ${{ github.event_name }} in + pull_request) MAINTAINER="${{ github.actor }} " ;; + push) MAINTAINER="${{ github.event.pusher.name }} <${{ github.event.pusher.email }}>" ;; + *) echo 2>&1 "ERROR: Unexpected GitHub Actions event"; exit 1 ;; + esac + + # Generate the RFC 5322 format date by hand instead of using date --rfc-email because that option doesn't exist + # on Ubuntu 16.04 and Debian 9 + RFC5322_TS=$(LC_TIME=en_US.UTF-8 date +'%a, %d %b %Y %H:%M:%S %z') + + # Generate the changelog file that Debian packages are required to have. + # See: https://www.debian.org/doc/manuals/maint-guide/dreq.en.html#changelog + if [ ! -d target/debian ]; then + mkdir -p target/debian + fi + echo "krill (${PKG_KRILL_VER}) unstable; urgency=medium" >target/debian/changelog + echo " * See: https://github.com/NLnetLabs/krill/releases/tag/v${KRILL_NEW_VER}" >>target/debian/changelog + echo " -- maintainer ${MAINTAINER} ${RFC5322_TS}" >>target/debian/changelog + + DEB_VER="${PKG_KRILL_VER}-1${OS_REL}" + cargo deb --variant ${OS_NAME}-${OS_REL} --deb-version ${DEB_VER} -v -- --locked ${EXTRA_BUILD_ARGS} + ;; + centos) + # Build and strip Krill as cargo generate-rpm doesn't do this for us + cargo build --release --locked -v ${EXTRA_BUILD_ARGS} + strip -s target/release/krill + + # Fix the version string to be used for the RPM package + sed -i -e "s/$KRILL_VER/$PKG_KRILL_VER/" Cargo.toml + + # Select the correct systemd service unit file for the target operating system + case ${MATRIX_IMAGE} in + ubuntu:xenial|centos:7) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-xenial.krill.service" ;; + ubuntu:bionic) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-bionic.krill.service" ;; + ubuntu:focal|centos:8) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-focal.krill.service" ;; + debian:stretch) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-stretch.krill.service" ;; + debian:buster) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-buster.krill.service" ;; + debian:bullseye) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-bullseye.krill.service" ;; + *) echo >&2 "ERROR: Unsupported matrix image value: '${MATRIX_IMAGE}'" ;; + esac + + # Copy the chosen systemd service unit file to where Cargo.toml expects it to be + mkdir -p target/rpm + cp pkg/common/${SYSTEMD_SERVICE_UNIT_FILE} target/rpm/krill.service + + cargo generate-rpm + ;; esac - # Generate the RFC 5322 format date by hand instead of using date --rfc-email - # because that option doesn't exist on Ubuntu 16.04 and Debian 9 - RFC5322_TS=$(LC_TIME=en_US.UTF-8 date +'%a, %d %b %Y %H:%M:%S %z') - - # Generate the changelog file that Debian packages are required to have. - # See: https://www.debian.org/doc/manuals/maint-guide/dreq.en.html#changelog - echo "krill (${DEB_KRILL_VER}) unstable; urgency=medium" >debian/changelog - echo " * See: https://github.com/NLnetLabs/krill/releases/tag/v${KRILL_VER}" >>debian/changelog - echo " -- maintainer ${MAINTAINER} ${RFC5322_TS}" >>debian/changelog - - DEB_VER="${DEB_KRILL_VER}-1${OS_REL}" - cargo deb --variant ${OS_NAME}-${OS_REL} --deb-version ${DEB_VER} -v -- --locked - - # See what Lintian thinks of our package. - - name: Verify the DEB package + # See what O/S specific linting tools think of our package. + - name: Verify the package run: | - lintian -v target/debian/*.deb + case ${OS_NAME} in + debian|ubuntu) + lintian -v target/debian/*.deb + ;; + centos) + # cargo generate-rpm creates RPMs that rpmlint considers to have + # errors so don't use the rpmlint exit code otherwise we will always + # abort the workflow. + rpmlint target/generate-rpm/*.rpm || true + ;; + esac - # Upload the produced DEB package. The artifact will be available - # via the GH Actions job summary and build log pages, but only to - # users logged in to GH with sufficient rights in this project. The - # uploaded artifact is also downloaded by the next job (see below) - # to sanity check that it can be installed and results in a working - # Krill installation. - - name: Upload DEB package + # Upload the produced package. The artifact will be available via the GH Actions job summary and build log pages, + # but only to users logged in to GH with sufficient rights in this project. The uploaded artifact is also downloaded + # by the next job (see below) to sanity check that it can be installed and results in a working Krill installation. + - name: Upload package uses: actions/upload-artifact@v2 with: name: ${{ env.OS_NAME }}_${{ env.OS_REL }} - path: target/debian/*.deb + path: | + target/debian/*.deb + target/generate-rpm/*.rpm - # Download and sanity check on target operating systems the packages created - # by previous jobs (see above). Don't test on GH runners as they come with - # lots of software and libraries pre-installed and thus are not representative - # of the actual deployment targets, nor do GH runners support all targets that - # we want to test. Don't test in Docker containers as they do not support - # systemd. - deb-pkg-test: - name: deb-pkg-test - needs: deb-pkg + # Download and sanity check on target operating systems the packages created by previous jobs (see above). Don't test + # on GH runners as they come with lots of software and libraries pre-installed and thus are not representative of the + # actual deployment targets, nor do GH runners support all targets that we want to test. Don't test in Docker + # containers as they do not support systemd. + pkg-test: + name: pkg-test + needs: pkg runs-on: ubuntu-latest strategy: fail-fast: false @@ -224,23 +314,27 @@ jobs: - 'debian:stretch' # debian/9 - 'debian:buster' # debian/10 - 'debian:bullseye' # debian/11 + - 'centos:7' + - 'centos:8' mode: - 'fresh-install' - 'upgrade-from-published' exclude: - image: 'debian:bullseye' mode: 'upgrade-from-published' + - image: 'centos:7' + mode: 'upgrade-from-published' + - image: 'centos:8' + mode: 'upgrade-from-published' steps: - # Set some environment variables that will be available to "run" steps below - # in this job, and some output variables that will be available in GH Action - # step definitions below. + # Set some environment variables that will be available to "run" steps below in this job, and some output variables + # that will be available in GH Action step definitions below. - name: Set vars id: setvars shell: bash run: | - # Get the operating system and release name (e.g. ubuntu and xenial) from - # the image name (e.g. ubuntu:xenial) by extracting only the parts before - # and after but not including the colon: + # Get the operating system and release name (e.g. ubuntu and xenial) from the image name (e.g. ubuntu:xenial) by + # extracting only the parts before and after but not including the colon: OS_NAME=${MATRIX_IMAGE%:*} OS_REL=${MATRIX_IMAGE#*:} @@ -250,7 +344,7 @@ jobs: env: MATRIX_IMAGE: ${{ matrix.image }} - - name: Download DEB package + - name: Download package uses: actions/download-artifact@v2 with: name: ${{ env.OS_NAME }}_${{ env.OS_REL }} @@ -267,19 +361,24 @@ jobs: run: | sg lxd -c "lxc info" + # Use of IPv6 sometimes prevents yum update being able to resolve + # mirrorlist.centos.org. + - name: Disable LXD assignment of IPv6 addresses + run: | + sg lxd -c "lxc network set lxdbr0 ipv6.address none" + - name: Launch LXC container run: | - - # security.nesting=true is needed to avoid error "Failed to set up mount - # namespacing: Permission denied" in a Debian 10 container. + # security.nesting=true is needed to avoid error "Failed to set up mount namespacing: Permission denied" in a + # Debian 10 container. sg lxd -c "lxc launch ${LXC_IMAGE} -c security.nesting=true testcon" - # Run apt-get update and install man and sudo support (missing in some LXC/LXD - # O/S images) but first wait for cloud-init to finish otherwise the network - # isn't yet ready. Don't use cloud-init status --wait as that isn't supported - # on older O/S's like Ubuntu 16.04 and Debian 9. Use the sudo package provided - # configuration files otherwise when using sudo we get an error that the root - # user isn't allowed to use sudo. + # Run package update and install man and sudo support (missing in some + # LXC/LXD O/S images) but first wait for cloud-init to finish otherwise the + # network isn't yet ready. Don't use cloud-init status --wait as that isn't + # supported on older O/S's like Ubuntu 16.04 and Debian 9. Use the sudo + # package provided configuration files otherwise when using sudo we get an + # error that the root user isn't allowed to use sudo. - name: Prepare container shell: bash run: | @@ -287,29 +386,66 @@ jobs: while ! sudo lxc exec testcon -- ls -la /var/lib/cloud/data/result.json; do sleep 1s done - sg lxd -c "lxc exec testcon -- apt-get update" - sg lxd -c "lxc exec testcon -- apt-get install -y -o Dpkg::Options::=\"--force-confnew\" apt-transport-https gnupg2 man sudo wget" - - name: Copy DEB into LXC container + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get update" + sg lxd -c "lxc exec testcon -- apt-get install -y -o Dpkg::Options::=\"--force-confnew\" apt-transport-https ca-certificates man sudo wget" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum update -y" + sg lxd -c "lxc exec testcon -- yum install -y man" + ;; + esac + + - name: Copy package into the LXC container run: | - DEB_FILE=$(ls -1 *.deb) - sg lxd -c "lxc file push ${DEB_FILE} testcon/tmp/" - echo "DEB_FILE=${DEB_FILE}" >> $GITHUB_ENV + case ${OS_NAME} in + debian|ubuntu) + DEB_FILE=$(ls -1 debian/*.deb) + sg lxd -c "lxc file push ${DEB_FILE} testcon/tmp/" + echo "PKG_FILE=$(basename $DEB_FILE)" >> $GITHUB_ENV + ;; + centos) + RPM_FILE=$(ls -1 generate-rpm/*.rpm) + sg lxd -c "lxc file push ${RPM_FILE} testcon/tmp/" + echo "PKG_FILE=$(basename $RPM_FILE)" >> $GITHUB_ENV + ;; + esac - - name: Install published DEB package + - name: Install previously published package if: ${{ matrix.mode == 'upgrade-from-published' }} run: | - echo "deb [arch=amd64] https://packages.nlnetlabs.nl/linux/${OS_NAME}/ ${OS_REL} main" >$HOME/nlnetlabs.list - sg lxd -c "lxc file push $HOME/nlnetlabs.list testcon/etc/apt/sources.list.d/" - sg lxd -c "lxc exec testcon -- wget -q https://packages.nlnetlabs.nl/aptkey.asc" - sg lxd -c "lxc exec testcon -- apt-key add ./aptkey.asc" - sg lxd -c "lxc exec testcon -- apt update" - sg lxd -c "lxc exec testcon -- apt install -y krill" + case ${OS_NAME} in + debian|ubuntu) + echo "deb [arch=amd64] https://packages.nlnetlabs.nl/linux/${OS_NAME}/ ${OS_REL} main" >$HOME/nlnetlabs.list + sg lxd -c "lxc file push $HOME/nlnetlabs.list testcon/etc/apt/sources.list.d/" + sg lxd -c "lxc exec testcon -- wget -q https://packages.nlnetlabs.nl/aptkey.asc" + sg lxd -c "lxc exec testcon -- apt-key add ./aptkey.asc" + sg lxd -c "lxc exec testcon -- apt update" + sg lxd -c "lxc exec testcon -- apt install -y krill" + ;; + centos) + rpm --import https://packages.nlnetlabs.nl/aptkey.asc + cat "[nlnetlabs]" >/etc/yum.repos.d/nlnetlabs.repo + cat "name=NLnet Labs" >>/etc/yum.repos.d/nlnetlabs.repo + cat "baseurl=https://packages.nlnetlabs.nl/linux/centos/$releasever/main/$basearch" >>/etc/yum.repos.d/nlnetlabs.repo + cat "enabled=1" >>/etc/yum.repos.d/nlnetlabs.repo + yum install -y krill + ;; + esac - - name: Install new DEB package + - name: Install new package if: ${{ matrix.mode == 'fresh-install' }} run: | - sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${DEB_FILE}" + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${PKG_FILE}" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum install -y /tmp/${PKG_FILE}" + ;; + esac - name: Test installed packages run: | @@ -344,10 +480,17 @@ jobs: echo -e "\nKRILL MAN PAGE:" sg lxd -c "lxc exec testcon -- man -P cat krill" - - name: Install new DEB package + - name: Install new package if: ${{ matrix.mode == 'upgrade-from-published' }} run: | - sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${DEB_FILE}" + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${PKG_FILE}" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum install -y /tmp/${PKG_FILE}" + ;; + esac - name: Test installed packages if: ${{ matrix.mode == 'upgrade-from-published' }} diff --git a/.rpm/krill.spec b/.rpm/krill.spec deleted file mode 100644 index 20250379..00000000 --- a/.rpm/krill.spec +++ /dev/null @@ -1,32 +0,0 @@ -%define __spec_install_post %{nil} -%define __os_install_post %{_dbpath}/brp-compress -%define debug_package %{nil} - -Name: krill -Summary: Resource Public Key Infrastructure (RPKI) daemon -Version: @@VERSION@@ -Release: @@RELEASE@@ -License: MPLv2.0 -Group: Applications/System -Source0: %{name}-%{version}.tar.gz -URL: https://www.nlnetlabs.nl/projects/rpki/krill/ - -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root - -%description -%{summary} - -%prep -%setup -q - -%install -rm -rf %{buildroot} -mkdir -p %{buildroot} -cp -a * %{buildroot} - -%clean -rm -rf %{buildroot} - -%files -%defattr(-,root,root,-) -%{_bindir}/* diff --git a/Cargo.toml b/Cargo.toml index 6e984567..ceffedb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,11 +105,11 @@ ctrlc = "^3.1" name = "krill" priority = "optional" section = "net" -extended-description-file = "debian/description.txt" +extended-description-file = "pkg/debian/description.txt" license-file = ["LICENSE", "0"] -depends = "$auto, adduser, libssl1.1" -maintainer-scripts = "debian/" -changelog = "debian/changelog" # this will be generated by the pkg workflow +depends = "$auto, passwd, libssl1.1" +maintainer-scripts = "pkg/debian/" +changelog = "target/debian/changelog" # this will be generated by the pkg workflow copyright = "Copyright (c) 2019, NLnet Labs. All rights reserved." assets = [ ["target/release/krill", "/usr/bin/krill", "755"], @@ -117,9 +117,9 @@ assets = [ ["defaults/krill.conf", "/usr/share/doc/krill/krill.conf", "644"], ["doc/krill.1", "/usr/share/man/man1/krill.1", "644"], ["doc/krillc.1", "/usr/share/man/man1/krillc.1", "644"], - ["debian/krill.service.preset", "/lib/systemd/system-preset/50-krill.preset", "644"], + ["pkg/common/krill.service.preset", "/lib/systemd/system-preset/50-krill.preset", "644"], ] -systemd-units = { unit-name = "krill", enable = false } +systemd-units = { unit-name = "krill", unit-scripts = "pkg/common", enable = false } # Variant of the Debian packaging configuration that: # a) statically links with OpenSSL when building a Debian package because the @@ -130,7 +130,7 @@ systemd-units = { unit-name = "krill", enable = false } # support newer features supported by Ubuntu 18.04 and 20.04. [package.metadata.deb.variants.ubuntu-xenial] features = [ "static-openssl" ] -depends = "$auto, adduser" +depends = "$auto, passwd" [package.metadata.deb.variants.ubuntu-bionic] @@ -138,7 +138,7 @@ depends = "$auto, adduser" [package.metadata.deb.variants.debian-stretch] features = [ "static-openssl" ] -depends = "$auto, adduser" +depends = "$auto, passwd" [package.metadata.deb.variants.debian-buster] @@ -146,3 +146,33 @@ depends = "$auto, adduser" # END DEBIAN PACKAGING # ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# START RPM PACKAGING +# +# Configurations for the cargo-generate-rpm cargo plugin which builds RPM +# packages in target/generate-rpm/ when invoked with: cargo generate-rpm +# +[package.metadata.generate-rpm] +# See: https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing +license = "MPLv2.0" +assets = [ + { source = "target/release/krill", dest = "/usr/bin/krill", mode = "755" }, + { source = "target/release/krillc", dest = "/usr/bin/krillc", mode = "755" }, + { source = "target/rpm/krill.service", dest = "/lib/systemd/system/krill.service", mode = "644" }, + { source = "defaults/krill.conf", dest = "/usr/share/doc/krill/krill.conf", mode = "644", doc = true }, + { source = "doc/krill.1", dest = "/usr/share/man/man1/krill.1", mode = "644", doc = true }, + { source = "doc/krillc.1", dest = "/usr/share/man/man1/krillc.1", mode = "644", doc = true }, + { source = "pkg/common/krill.service.preset", dest = "/lib/systemd/system-preset/50-krill.preset", mode = "644" }, + { source = "pkg/rpm/postinst", dest = "/usr/share/krill/rpm/postinst", mode = "755" }, + { source = "pkg/rpm/postrm", dest = "/usr/share/krill/rpm/postrm", mode = "755" }, +] +post_install_script = "/usr/share/krill/rpm/postinst $*" +post_uninstall_script = "/usr/share/krill/rpm/postrm $*" + +# ensure that the useradd tool is present by depending on its package +[package.metadata.generate-rpm.requires] +shadow-utils = "*" + +# END RPM PACKAGING +# ------------------------------------------------------------------------------ diff --git a/debian/krill-debian-bullseye.krill.service b/pkg/common/krill-debian-bullseye.krill.service similarity index 100% rename from debian/krill-debian-bullseye.krill.service rename to pkg/common/krill-debian-bullseye.krill.service diff --git a/debian/krill-debian-buster.krill.service b/pkg/common/krill-debian-buster.krill.service similarity index 100% rename from debian/krill-debian-buster.krill.service rename to pkg/common/krill-debian-buster.krill.service diff --git a/debian/krill-debian-stretch.krill.service b/pkg/common/krill-debian-stretch.krill.service similarity index 100% rename from debian/krill-debian-stretch.krill.service rename to pkg/common/krill-debian-stretch.krill.service diff --git a/debian/krill-ubuntu-bionic.krill.service b/pkg/common/krill-ubuntu-bionic.krill.service similarity index 100% rename from debian/krill-ubuntu-bionic.krill.service rename to pkg/common/krill-ubuntu-bionic.krill.service diff --git a/debian/krill-ubuntu-focal.krill.service b/pkg/common/krill-ubuntu-focal.krill.service similarity index 100% rename from debian/krill-ubuntu-focal.krill.service rename to pkg/common/krill-ubuntu-focal.krill.service diff --git a/debian/krill-ubuntu-xenial.krill.service b/pkg/common/krill-ubuntu-xenial.krill.service similarity index 100% rename from debian/krill-ubuntu-xenial.krill.service rename to pkg/common/krill-ubuntu-xenial.krill.service diff --git a/debian/krill.service.preset b/pkg/common/krill.service.preset similarity index 100% rename from debian/krill.service.preset rename to pkg/common/krill.service.preset diff --git a/debian/description.txt b/pkg/debian/description.txt similarity index 100% rename from debian/description.txt rename to pkg/debian/description.txt diff --git a/debian/postinst b/pkg/debian/postinst similarity index 100% rename from debian/postinst rename to pkg/debian/postinst diff --git a/debian/postrm b/pkg/debian/postrm similarity index 100% rename from debian/postrm rename to pkg/debian/postrm diff --git a/debian/preinst b/pkg/debian/preinst similarity index 100% rename from debian/preinst rename to pkg/debian/preinst diff --git a/pkg/rpm/postinst b/pkg/rpm/postinst new file mode 100755 index 00000000..e12f9448 --- /dev/null +++ b/pkg/rpm/postinst @@ -0,0 +1,66 @@ +#!/bin/bash -e +# Script based on the RPM %systemd_post scriptlet. See: +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd +# - https://cgit.freedesktop.org/systemd/systemd/tree/src/core/macros.systemd.in + +KRILL_USER=krill +KRILL_GROUP=${KRILL_USER} +KRILL_HOME_DIR="/var/lib/krill" +KRILL_HOME_DIR_PERMS=700 +KRILL_CONF="/etc/krill.conf" +KRILL_DATA="${KRILL_HOME_DIR}/data/" + +if [ $EUID -ne 0 ]; then + echo >&2 "ERROR: RTRTR postinst script must be run as root" + exit 1 +fi + +create_user() { + # According to the CentOS 7 useradd man page: + # --user-group causes a group by the same name as the user to be created + # --create-home should force creation of a home dir even for a system account. + useradd --system --home-dir ${KRILL_HOME_DIR} --system --create-home --user-group ${KRILL_USER} + + # Ensure that the home directory has the correct ownership + chown -R ${KRILL_USER}:${KRILL_GROUP} ${KRILL_HOME_DIR} + + # Ensure that the home directory has the correct permissions + chmod ${KRILL_HOME_DIR_PERMS} ${KRILL_HOME_DIR} +} + +init_systemd_service() { + systemctl preset krill.service 2>&1 || : +} + +generate_password() { + # Tries not to depend on too many other commmands + # being installed. + date | md5sum | awk '{print $1}' +} + +create_first_time_configuration() { + if [ ! -f "${KRILL_CONF}" ]; then + # generate a token for authenticating with Krill + ADMIN_TOKEN="$(generate_password)" + + # generate a config file using our preferred filesystem locations + # and generated admin token + # note: we don't configure Krill to store its PID file under /var/run/ + # because that requires root privileges potentially at least once per + # boot, and Krill doesn't drop privileges yet so when run as a non-root + # user has no right to create the file or missing /var/run/subdir. + # See: https://stackoverflow.com/a/28312577 + krillc config simple \ + --data "${KRILL_DATA}" \ + --token "${ADMIN_TOKEN}" | + sed -e "s|^\(### log_type.\+\)|\1\nlog_type = \"syslog\"|" \ + > "${KRILL_CONF}" + fi +} + +if [ $1 -eq 1 ] ; then + # Initial installation + create_user + init_systemd_service + create_first_time_configuration +fi \ No newline at end of file diff --git a/pkg/rpm/postrm b/pkg/rpm/postrm new file mode 100644 index 00000000..9b3dc5e9 --- /dev/null +++ b/pkg/rpm/postrm @@ -0,0 +1,9 @@ +#!/bin/bash -e +# Script based on the RPM %systemd_postun scriptlet. See: +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd +# - https://cgit.freedesktop.org/systemd/systemd/tree/src/core/macros.systemd.in + +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + systemctl try-restart krill.service >/dev/null 2>&1 || : +fi \ No newline at end of file From 40388d810604b09b1956587c0a9d4cbafffa8bab Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Mon, 12 Jul 2021 16:56:30 +0200 Subject: [PATCH 05/17] Add some images for use in the development docs. --- .../images/cypress-welcome-popup.png | Bin 0 -> 37698 bytes .../multi_user/images/cyress-test-running.png | Bin 0 -> 101489 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/development/multi_user/images/cypress-welcome-popup.png create mode 100644 doc/development/multi_user/images/cyress-test-running.png diff --git a/doc/development/multi_user/images/cypress-welcome-popup.png b/doc/development/multi_user/images/cypress-welcome-popup.png new file mode 100644 index 0000000000000000000000000000000000000000..d68f271c59b25f7ab106a381e2de4c5ac15325d9 GIT binary patch literal 37698 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 literal 0 HcmV?d00001 diff --git a/doc/development/multi_user/images/cyress-test-running.png b/doc/development/multi_user/images/cyress-test-running.png new file mode 100644 index 0000000000000000000000000000000000000000..570326a6d6b15c49901f3f19ac648d44cb5a475a GIT binary patch literal 101489 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
YYb-gAJXHYivx&!9P5pQ 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 Date: Mon, 12 Jul 2021 17:00:41 +0200 Subject: [PATCH 06/17] Fix doc image filename and trim doc image border. --- ...t-running.png => cypress-test-running.png} | Bin .../images/cypress-welcome-popup.png | Bin 37698 -> 51459 bytes 2 files changed, 0 insertions(+), 0 deletions(-) rename doc/development/multi_user/images/{cyress-test-running.png => cypress-test-running.png} (100%) diff --git a/doc/development/multi_user/images/cyress-test-running.png b/doc/development/multi_user/images/cypress-test-running.png similarity index 100% rename from doc/development/multi_user/images/cyress-test-running.png rename to doc/development/multi_user/images/cypress-test-running.png diff --git a/doc/development/multi_user/images/cypress-welcome-popup.png b/doc/development/multi_user/images/cypress-welcome-popup.png index d68f271c59b25f7ab106a381e2de4c5ac15325d9..ce3bbc907b1d2122d86c0077940423e0725d2986 100644 GIT binary patch literal 51459 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 literal 37698 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 From cdaf0f35b0518eff64b593b79f9e8858682c6398 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Mon, 12 Jul 2021 17:01:26 +0200 Subject: [PATCH 07/17] Add some images to demonstrate Cyprss interactive UI testing with Krill. --- doc/development/multi_user/testing.md | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/doc/development/multi_user/testing.md b/doc/development/multi_user/testing.md index 6dc01cae..3b14c867 100644 --- a/doc/development/multi_user/testing.md +++ b/doc/development/multi_user/testing.md @@ -25,11 +25,33 @@ this has not yet been verified as working on Mac OS. Cypress in turn is driven b As UI based tests can be quite slow they are gated behind their own `ui-tests` feature, which also has the benefit that users without a working Docker setup are still able to run `cargo test`. -To run the UI tests one must therefore do: `cargo test --features ui-tests`. +To run the UI tests one must therefore do: + +``` +cargo test --features ui-tests +``` Cypress has a very useful interactive test run mode which can be launched like so: ``` $ xhost + -$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests -``` \ No newline at end of file +$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests +``` + +You want the `` because you want Krill to be setup correctly to run a particular test that you will then run interactively, you don't want Krill to run all tests and constantly be changing the backend state as a result while you try to use Cypress to run a single test suite that has expectations about the state that Krill is in. + +For example you might do: +``` +$ xhost + +$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests multi_user_config_file_with_ta +``` + +After a short delay a browser window should open with a Cypress welcome message something like this: + +![Cypress welcome popup](images/cypress-welcome-popup.png) + +Dismiss the message and then click on the `multi_user_config_file_with_ta.js` test in the tree of tests that is shown to you, i.e. run the same test as you invoked with `cargo test` so that Krill has the expected configuration and starting conditions. + +You should then see something like this: + +![Cypress test running](images/cypress-test-running.png) From fbabf8e452cd6ee2836849e83b6959408b1181a0 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 11:57:19 +0200 Subject: [PATCH 08/17] Remove superfluous RwLock around KrillServer (it has its own locks). (#606) --- src/daemon/http/server.rs | 137 +++++++++++++++++--------------------- src/daemon/krillserver.rs | 4 +- 2 files changed, 64 insertions(+), 77 deletions(-) diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 1e00273d..c818d242 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -12,8 +12,6 @@ use std::sync::Arc; use bytes::Bytes; use serde::Serialize; -use tokio::sync::RwLock; - use futures::TryFutureExt; use hyper::header::HeaderName; use hyper::http::HeaderValue; @@ -51,7 +49,7 @@ use crate::{ //------------ State ----------------------------------------------------- -pub type State = Arc>; +pub type State = Arc; pub fn parse_config() -> KrillResult { Config::create().map_err(|e| Error::Custom(format!("Could not parse config: {}", e))) @@ -107,7 +105,7 @@ pub async fn start_krill_daemon(config: Arc) -> Result<(), Error> { ::std::process::exit(0); } - let state = Arc::new(RwLock::new(krill)); + let state = Arc::new(krill); let service = make_service_fn(move |_| { let state = state.clone(); @@ -336,7 +334,7 @@ pub async fn health(req: Request) -> RoutingResult { pub async fn metrics(req: Request) -> RoutingResult { if req.is_get() && req.path().segment().starts_with("metrics") { let server = req.state(); - let server = server.read().await; + let server = server; struct AllBgpStats { announcements_valid: HashMap, @@ -597,7 +595,7 @@ pub async fn rfc8181(req: Request) -> RoutingResult { Err(e) => return render_error(e), }; - let read = state.read().await; + let read = state; match read.rfc8181(publisher, bytes) { Ok(bytes) => Ok(HttpResponse::rfc8181(bytes.to_vec())), Err(e) => render_error(e), @@ -621,14 +619,14 @@ async fn ta(req: Request) -> RoutingResult { } pub async fn tal(req: Request) -> RoutingResult { - match req.state().read().await.ta().await { + match req.state().ta().await { Ok(ta) => Ok(HttpResponse::text(format!("{}", ta.tal()).into_bytes())), Err(_) => render_unknown_resource(), } } pub async fn ta_cer(req: Request) -> RoutingResult { - match req.state().read().await.trust_anchor_cert().await { + match req.state().trust_anchor_cert().await { Some(cert) => Ok(HttpResponse::cert(cert.to_captured().to_vec())), None => render_unknown_resource(), } @@ -653,7 +651,7 @@ pub async fn rfc6492(req: Request) -> RoutingResult { Ok(bytes) => bytes, Err(e) => return render_error(e), }; - let krill_server = state.read().await; + let krill_server = state; match krill_server.rfc6492(ca, bytes, &actor).await { Ok(bytes) => Ok(HttpResponse::rfc6492(bytes.to_vec())), Err(e) => render_error(e), @@ -667,9 +665,9 @@ pub async fn rfc6492(req: Request) -> RoutingResult { async fn stats(req: Request) -> RoutingResult { match *req.method() { Method::GET => match req.path().full() { - "/stats/info" => render_json(req.state().read().await.server_info()), - "/stats/repo" => render_json_res(req.state().read().await.repo_stats()), - "/stats/cas" => render_json_res(req.state().read().await.cas_stats().await), + "/stats/info" => render_json(req.state().server_info()), + "/stats/repo" => render_json_res(req.state().repo_stats()), + "/stats/cas" => render_json_res(req.state().cas_stats().await), _ => Err(req), }, _ => Err(req), @@ -930,11 +928,11 @@ async fn api_publication_server(req: Request, path: &mut RequestPath) -> Routing Method::POST => { let state = req.state.clone(); match req.json().await { - Ok(uris) => render_empty_res(state.write().await.repository_init(uris)), + Ok(uris) => render_empty_res(state.repository_init(uris)), Err(e) => render_error(e), } } - Method::DELETE => render_empty_res(req.state.write().await.repository_clear()), + Method::DELETE => render_empty_res(req.state.repository_clear()), _ => render_unknown_method(), }, _ => render_unknown_method(), @@ -974,10 +972,7 @@ pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> Routin let seconds = seconds.unwrap_or(""); match i64::from_str(seconds) { Ok(seconds) => render_json_res( - req.state() - .read() - .await - .repo_stats() + req.state().repo_stats() .map(|stats| PublisherList::build(&stats.stale_publishers(seconds))), ), Err(_) => render_error(Error::ApiInvalidSeconds), @@ -989,11 +984,7 @@ pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> Routin pub async fn api_list_pbl(req: Request) -> RoutingResult { aa!(req, Permission::PUB_LIST, { render_json_res( - req.state() - .read() - .await - .publishers() - .map(|publishers| PublisherList::build(&publishers)), + req.state().publishers().map(|publishers| PublisherList::build(&publishers)), ) }) } @@ -1004,7 +995,7 @@ pub async fn api_add_pbl(req: Request) -> RoutingResult { let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(pbl) => render_json_res(server.write().await.add_publisher(pbl, &actor)), + Ok(pbl) => render_json_res(server.add_publisher(pbl, &actor)), Err(e) => render_error(e), } }) @@ -1015,7 +1006,7 @@ pub async fn api_add_pbl(req: Request) -> RoutingResult { pub async fn api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult { aa!(req, Permission::PUB_DELETE, publisher.clone(), { let actor = req.actor(); - render_empty_res(req.state().write().await.remove_publisher(publisher, &actor)) + render_empty_res(req.state().remove_publisher(publisher, &actor)) }) } @@ -1025,7 +1016,7 @@ pub async fn api_show_pbl(req: Request, publisher: Handle) -> RoutingResult { req, Permission::PUB_READ, publisher.clone(), - render_json_res(req.state().read().await.get_publisher(&publisher)) + render_json_res(req.state().get_publisher(&publisher)) ) } @@ -1050,7 +1041,7 @@ pub async fn api_repository_response_json(req: Request, publisher: Handle) -> Ro } async fn repository_response(req: &Request, publisher: &Handle) -> Result { - req.state().read().await.repository_response(publisher) + req.state().repository_response(publisher) } pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult { @@ -1058,7 +1049,7 @@ pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResu let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(child_req) => render_json_res(server.read().await.ca_add_child(&parent, child_req, &actor).await), + Ok(child_req) => render_json_res(server.ca_add_child(&parent, child_req, &actor).await), Err(e) => render_error(e), } }) @@ -1069,7 +1060,7 @@ async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> Ro let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(child_req) => render_empty_res(server.read().await.ca_child_update(&ca, child, child_req, &actor).await), + Ok(child_req) => render_empty_res(server.ca_child_update(&ca, child, child_req, &actor).await), Err(e) => render_error(e), } }) @@ -1078,7 +1069,7 @@ async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> Ro pub async fn api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_child_remove(&ca, child, &actor).await) + render_empty_res(req.state().ca_child_remove(&ca, child, &actor).await) }) } @@ -1087,7 +1078,7 @@ async fn api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> Rout req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_child_show(&ca, &child).await) + render_json_res(req.state().ca_child_show(&ca, &child).await) ) } @@ -1096,7 +1087,7 @@ async fn api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_parent_contact(&ca, child.clone()).await) + render_json_res(req.state().ca_parent_contact(&ca, child.clone()).await) ) } @@ -1105,13 +1096,13 @@ async fn api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_parent_response(&ca, child.clone()).await) + render_json_res(req.state().ca_parent_response(&ca, child.clone()).await) ) } pub async fn api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult { aa!(req, Permission::CA_READ, ca.clone(), { - match req.state().read().await.ca_parent_response(&ca, child.clone()).await { + match req.state().ca_parent_response(&ca, child.clone()).await { Ok(res) => Ok(HttpResponse::xml(res.encode_vec())), Err(e) => render_error(e), } @@ -1124,7 +1115,7 @@ async fn api_all_ca_issues(req: Request) -> RoutingResult { match *req.method() { Method::GET => aa!(req, Permission::CA_READ, { let actor = req.actor(); - render_json_res(req.state().read().await.all_ca_issues(&actor).await) + render_json_res(req.state().all_ca_issues(&actor).await) }), _ => render_unknown_method(), } @@ -1137,7 +1128,7 @@ async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult { req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_issues(&ca).await) + render_json_res(req.state().ca_issues(&ca).await) ), _ => render_unknown_method(), } @@ -1146,7 +1137,7 @@ async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult { async fn api_cas_list(req: Request) -> RoutingResult { aa!(req, Permission::CA_LIST, { let actor = req.actor(); - render_json_res(req.state().read().await.ca_list(&actor)) + render_json_res(req.state().ca_list(&actor)) }) } @@ -1155,7 +1146,7 @@ pub async fn api_ca_init(req: Request) -> RoutingResult { let state = req.state().clone(); match req.json().await { - Ok(ca_init) => render_empty_res(state.write().await.ca_init(ca_init).await), + Ok(ca_init) => render_empty_res(state.ca_init(ca_init).await), Err(e) => render_error(e), } }) @@ -1165,7 +1156,7 @@ async fn api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingR match *req.method() { Method::POST => aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_update_id(ca, &actor).await) + render_empty_res(req.state().ca_update_id(ca, &actor).await) }), Method::GET => match path.next() { Some("child_request.xml") => api_ca_child_req_xml(req, ca).await, @@ -1183,7 +1174,7 @@ async fn api_ca_info(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_info(&handle).await) + render_json_res(req.state().ca_info(&handle).await) ) } @@ -1193,7 +1184,7 @@ async fn api_ca_delete(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_DELETE, handle.clone(), - render_json_res(req.state().read().await.ca_delete(&handle, &actor).await) + render_json_res(req.state().ca_delete(&handle, &actor).await) ) } @@ -1202,7 +1193,7 @@ async fn api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_my_parent_contact(&ca, &parent).await) + render_json_res(req.state().ca_my_parent_contact(&ca, &parent).await) ) } @@ -1211,7 +1202,7 @@ async fn api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult { req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_my_parent_statuses(&ca).await) + render_json_res(req.state().ca_my_parent_statuses(&ca).await) ) } @@ -1257,7 +1248,7 @@ async fn api_ca_history_commands(req: Request, path: &mut RequestPath, handle: H if let Some(before) = path.path_arg() { crit.set_before(before); } - match req.state().read().await.ca_history(&handle, crit).await { + match req.state().ca_history(&handle, crit).await { Ok(history) => render_json(history), Err(e) => render_error(e), } @@ -1279,7 +1270,7 @@ async fn api_ca_command_details(req: Request, path: &mut RequestPath, handle: Ha match path.path_arg() { Some(key) => match *req.method() { Method::GET => aa!(req, Permission::CA_READ, handle.clone(), { - match req.state().read().await.ca_command_details(&handle, key) { + match req.state().ca_command_details(&handle, key) { Ok(details) => render_json(details), Err(e) => match e { Error::AggregateStoreError(AggregateStoreError::UnknownCommand(_, _)) => { @@ -1326,7 +1317,7 @@ async fn api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult { } async fn ca_child_req(req: &Request, handle: &Handle) -> Result { - req.state().read().await.ca_child_req(handle).await + req.state().ca_child_req(handle).await } async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult { @@ -1335,7 +1326,7 @@ async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResul req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_publisher_req(&handle).await) + render_json_res(req.state().ca_publisher_req(&handle).await) ), _ => render_unknown_method(), } @@ -1347,7 +1338,7 @@ async fn api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult req, Permission::CA_READ, handle.clone(), - match req.state().read().await.ca_publisher_req(&handle).await { + match req.state().ca_publisher_req(&handle).await { Ok(res) => Ok(HttpResponse::xml(res.encode_vec())), Err(e) => render_error(e), } @@ -1361,7 +1352,7 @@ async fn api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_repo_details(&handle).await) + render_json_res(req.state().ca_repo_details(&handle).await) ) } @@ -1371,7 +1362,7 @@ async fn api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_repo_status(&handle).await) + render_json_res(req.state().ca_repo_status(&handle).await) ), _ => render_unknown_method(), } @@ -1404,7 +1395,7 @@ async fn api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult { .await .map(|bytes| extract_repository_contact(&handle, bytes)) { - Ok(Ok(update)) => render_empty_res(server.read().await.ca_repo_update(handle, update, &actor).await), + Ok(Ok(update)) => render_empty_res(server.ca_repo_update(handle, update, &actor).await), Ok(Err(e)) | Err(e) => render_error(e), } }) @@ -1422,11 +1413,7 @@ async fn api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: match extract_parent_ca_req(&ca, bytes, parent_override) { Ok(parent_req) => render_empty_res( - server - .read() - .await - .ca_parent_add_or_update(ca, parent_req, &actor) - .await, + server.ca_parent_add_or_update(ca, parent_req, &actor).await, ), Err(e) => render_error(e), } @@ -1468,7 +1455,7 @@ fn extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_parent_remove(ca, parent, &actor).await) + render_empty_res(req.state().ca_parent_remove(ca, parent, &actor).await) }) } @@ -1476,7 +1463,7 @@ async fn api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> Routi async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_keyroll_init(ca, &actor).await) + render_empty_res(req.state().ca_keyroll_init(ca, &actor).await) }) } @@ -1484,7 +1471,7 @@ async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult { async fn api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_keyroll_activate(ca, &actor).await) + render_empty_res(req.state().ca_keyroll_activate(ca, &actor).await) }) } @@ -1496,7 +1483,7 @@ async fn api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult { match req.json().await { Err(e) => render_error(e), - Ok(updates) => render_empty_res(state.read().await.ca_routes_update(ca, updates, &actor).await), + Ok(updates) => render_empty_res(state.ca_routes_update(ca, updates, &actor).await), } }) } @@ -1512,7 +1499,7 @@ async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult { match req.json::().await { Err(e) => render_error(e), Ok(updates) => { - let server = state.read().await; + let server = state; match server.ca_routes_bgp_dry_run(&ca, updates.clone()).await { Err(e) => { // update was rejected, return error @@ -1543,7 +1530,7 @@ async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult { /// show the route authorizations for this CA async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::ROUTES_READ, ca.clone(), { - match req.state().read().await.ca_routes_show(&ca).await { + match req.state().ca_routes_show(&ca).await { Ok(roas) => render_json(roas), Err(_) => render_unknown_resource(), } @@ -1554,25 +1541,25 @@ async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult { async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult { aa!(req, Permission::ROUTES_ANALYSIS, ca.clone(), { match path.next() { - Some("full") => render_json_res(req.state().read().await.ca_routes_bgp_analysis(&ca).await), + Some("full") => render_json_res(req.state().ca_routes_bgp_analysis(&ca).await), Some("dryrun") => match *req.method() { Method::POST => { let state = req.state.clone(); match req.json().await { Err(e) => render_error(e), - Ok(updates) => render_json_res(state.read().await.ca_routes_bgp_dry_run(&ca, updates).await), + Ok(updates) => render_json_res(state.ca_routes_bgp_dry_run(&ca, updates).await), } } _ => render_unknown_method(), }, Some("suggest") => match *req.method() { - Method::GET => render_json_res(req.state().read().await.ca_routes_bgp_suggest(&ca, None).await), + Method::GET => render_json_res(req.state().ca_routes_bgp_suggest(&ca, None).await), Method::POST => { let server = req.state().clone(); match req.json().await { Err(e) => render_error(e), Ok(resources) => { - render_json_res(server.read().await.ca_routes_bgp_suggest(&ca, Some(resources)).await) + render_json_res(server.ca_routes_bgp_suggest(&ca, Some(resources)).await) } } } @@ -1588,7 +1575,7 @@ async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle async fn api_republish_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { - render_empty_res(req.state().read().await.republish_all().await) + render_empty_res(req.state().republish_all().await) }), _ => render_unknown_method(), } @@ -1598,7 +1585,7 @@ async fn api_resync_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { let actor = req.actor(); - render_empty_res(req.state().read().await.resync_all(&actor).await) + render_empty_res(req.state().resync_all(&actor).await) }), _ => render_unknown_method(), } @@ -1609,7 +1596,7 @@ async fn api_refresh_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { let actor = req.actor(); - render_empty_res(req.state().read().await.cas_refresh_all(&actor).await) + render_empty_res(req.state().cas_refresh_all(&actor).await) }), _ => render_unknown_method(), } @@ -1621,7 +1608,7 @@ async fn rrdp(req: Request) -> RoutingResult { if !req.path().full().starts_with("/rrdp/") { Err(req) // Not for us } else { - let mut full_path: PathBuf = req.state.read().await.rrdp_base_path(); + let mut full_path: PathBuf = req.state.rrdp_base_path(); let (_, path) = req.path.remaining().split_at(1); let cache_seconds = if path.ends_with("notification.xml") { 60 } else { 86400 }; full_path.push(path); @@ -1674,7 +1661,7 @@ async fn api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult { req, Permission::RTA_LIST, ca.clone(), - render_json_res(req.state().read().await.rta_list(ca).await) + render_json_res(req.state().rta_list(ca).await) ) } @@ -1683,7 +1670,7 @@ async fn api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResu req, Permission::RTA_READ, ca.clone(), - render_json_res(req.state().read().await.rta_show(ca, name).await) + render_json_res(req.state().rta_show(ca, name).await) ) } @@ -1693,7 +1680,7 @@ async fn api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResu let state = req.state().clone(); match req.json().await { Err(e) => render_error(e), - Ok(request) => render_empty_res(state.read().await.rta_sign(ca, name, request, &actor).await), + Ok(request) => render_empty_res(state.rta_sign(ca, name, request, &actor).await), } }) } @@ -1704,7 +1691,7 @@ async fn api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> Routi let state = req.state().clone(); match req.json().await { - Ok(resources) => render_json_res(state.read().await.rta_multi_prep(ca, name, resources, &actor).await), + Ok(resources) => render_json_res(state.rta_multi_prep(ca, name, resources, &actor).await), Err(e) => render_error(e), } }) @@ -1715,7 +1702,7 @@ async fn api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> Routi let actor = req.actor(); let state = req.state().clone(); match req.json().await { - Ok(rta) => render_empty_res(state.read().await.rta_multi_cosign(ca, name, rta, &actor).await), + Ok(rta) => render_empty_res(state.rta_multi_cosign(ca, name, rta, &actor).await), Err(_) => render_error(Error::custom("Cannot decode RTA for co-signing")), } }) diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 3a3bc3f8..5153aa80 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -330,7 +330,7 @@ impl KrillServer { } /// Removes a publisher, blows up if it didn't exist. - pub fn remove_publisher(&mut self, publisher: PublisherHandle, actor: &Actor) -> KrillEmptyResult { + pub fn remove_publisher(&self, publisher: PublisherHandle, actor: &Actor) -> KrillEmptyResult { self.repo_manager.remove_publisher(publisher, actor) } @@ -604,7 +604,7 @@ impl KrillServer { self.ca_manager.get_ca(handle).await.map(|ca| ca.publisher_request()) } - pub async fn ca_init(&mut self, init: CertAuthInit) -> KrillEmptyResult { + pub async fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { let handle = init.unpack(); self.ca_manager.init_ca(&handle) } From 85378ff311621987fe1ac6a8efc75da3ce7c4ef5 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 11:58:31 +0200 Subject: [PATCH 09/17] Cache the repository content: work around slow deserialization. (#606) --- doc/development/05_repo_manager.md | 1 + src/daemon/http/mod.rs | 16 +++--- src/daemon/http/testbed.rs | 2 +- src/pubd/repository.rs | 78 +++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 27 deletions(-) diff --git a/doc/development/05_repo_manager.md b/doc/development/05_repo_manager.md index d389d33f..69b77da8 100644 --- a/doc/development/05_repo_manager.md +++ b/doc/development/05_repo_manager.md @@ -43,6 +43,7 @@ pub struct RepositoryAccessProxy { /// so that callers don't need to worry about storage details. #[derive(Debug)] pub struct RepositoryContentProxy { + cache: RwLock>>, store: RwLock, key: KeyStoreKey, } diff --git a/src/daemon/http/mod.rs b/src/daemon/http/mod.rs index 61538b3d..d585c65f 100644 --- a/src/daemon/http/mod.rs +++ b/src/daemon/http/mod.rs @@ -339,7 +339,7 @@ pub struct Request { impl Request { pub async fn new(request: hyper::Request, state: State) -> Self { let path = RequestPath::from_request(&request); - let actor = state.read().await.actor_from_request(&request); + let actor = state.actor_from_request(&request); Request { request, @@ -355,7 +355,7 @@ impl Request { pub async fn upgrade_from_anonymous(&mut self, actor_def: ActorDef) { if self.actor.is_anonymous() { - self.actor = self.state.read().await.actor_from_def(actor_def); + self.actor = self.state.actor_from_def(actor_def); info!( "Permitted anonymous actor to become actor '{}' for the duration of this request", self.actor.name() @@ -410,17 +410,17 @@ impl Request { } pub async fn api_bytes(self) -> Result { - let limit = self.state().read().await.limit_api(); + let limit = self.state().limit_api(); self.read_bytes(limit).await } pub async fn rfc6492_bytes(self) -> Result { - let limit = self.state().read().await.limit_rfc6492(); + let limit = self.state().limit_rfc6492(); self.read_bytes(limit).await } pub async fn rfc8181_bytes(self) -> Result { - let limit = self.state().read().await.limit_rfc8181(); + let limit = self.state().limit_rfc8181(); self.read_bytes(limit).await } @@ -488,15 +488,15 @@ impl Request { } pub async fn get_login_url(&self) -> KrillResult { - self.state.read().await.get_login_url() + self.state.get_login_url() } pub async fn login(&self) -> KrillResult { - self.state.read().await.login(&self.request) + self.state.login(&self.request) } pub async fn logout(&self) -> KrillResult { - self.state.read().await.logout(&self.request) + self.state.logout(&self.request) } } diff --git a/src/daemon/http/testbed.rs b/src/daemon/http/testbed.rs index ad5b85bb..07ead962 100644 --- a/src/daemon/http/testbed.rs +++ b/src/daemon/http/testbed.rs @@ -35,7 +35,7 @@ use crate::{commons::api::Handle, constants::ACTOR_DEF_TESTBED}; pub async fn testbed(mut req: Request) -> RoutingResult { if !req.path().full().starts_with("/testbed") { Err(req) // Not for us - } else if !req.state().read().await.testbed_enabled() { + } else if !req.state().testbed_enabled() { render_unknown_method() } else { // The testbed is intended to be used without being logged in but diff --git a/src/pubd/repository.rs b/src/pubd/repository.rs index 6d749260..d7e29522 100644 --- a/src/pubd/repository.rs +++ b/src/pubd/repository.rs @@ -49,6 +49,7 @@ use super::RepositoryAccessInitDetails; /// so that callers don't need to worry about storage details. #[derive(Debug)] pub struct RepositoryContentProxy { + cache: RwLock>>, store: RwLock, key: KeyStoreKey, } @@ -59,8 +60,15 @@ impl RepositoryContentProxy { let store = KeyValueStore::disk(work_dir, PUBSERVER_CONTENT_DIR)?; let store = RwLock::new(store); let key = KeyStoreKey::simple(format!("{}.json", PUBSERVER_DFLT)); + let cache = RwLock::new(None); - Ok(RepositoryContentProxy { store, key }) + let proxy = RepositoryContentProxy { cache, store, key }; + + // Warm the cache, ignore error indicating there is no content. + info!("Warming the repository content cache, this can take a minute for large repositories."); + let _ = proxy.read_content(); + + Ok(proxy) } // Initialize @@ -68,23 +76,31 @@ impl RepositoryContentProxy { if self.store.read().unwrap().has(&self.key)? { Err(Error::RepositoryServerAlreadyInitialized) } else { - let (rrdp_base_uri, rsync_jail) = uris.unpack(); + // initialize new repo content + let repository_content = { + let (rrdp_base_uri, rsync_jail) = uris.unpack(); - let publishers = HashMap::new(); + let publishers = HashMap::new(); - let session = RrdpSession::default(); - let stats = RepoStats::new(session); + let session = RrdpSession::default(); + let stats = RepoStats::new(session); - let mut repo_dir = work_dir.to_path_buf(); - repo_dir.push(REPOSITORY_DIR); + let mut repo_dir = work_dir.to_path_buf(); + repo_dir.push(REPOSITORY_DIR); - let rrdp = RrdpServer::create(rrdp_base_uri, &repo_dir, session); - let rsync = RsyncdStore::new(rsync_jail, &repo_dir); + let rrdp = RrdpServer::create(rrdp_base_uri, &repo_dir, session); + let rsync = RsyncdStore::new(rsync_jail, &repo_dir); - let repo = RepositoryContent::new(publishers, rrdp, rsync, stats); + RepositoryContent::new(publishers, rrdp, rsync, stats) + }; + // Store newly initialized repo content on disk let store = self.store.write().unwrap(); - store.store(&self.key, &repo)?; + store.store(&self.key, &repository_content)?; + + // Store newly initialized repo content in cache + let mut cache = self.cache.write().unwrap(); + cache.replace(Arc::new(repository_content)); Ok(()) } @@ -151,22 +167,48 @@ impl RepositoryContentProxy { self.write(|content| content.session_reset(config)) } + fn write KrillResult<()>>(&self, op: F) -> KrillResult<()> { + // If there is any existing content, then we can assume that the cache + // has it - because it's initialized when we read the content during + // initialization. let store = self.store.write().unwrap(); - let mut content: RepositoryContent = store.get(&self.key)?.ok_or(Error::RepositoryServerNotInitialized)?; + let mut cache = self.cache.write().unwrap(); - op(&mut content)?; + let content_arc: &mut Arc = cache.as_mut().ok_or(Error::RepositoryServerNotInitialized)?; + let content = Arc::::get_mut(content_arc).unwrap(); - store.store(&self.key, &content)?; + op(content)?; + + store.store(&self.key, content)?; Ok(()) } - fn read_content(&self) -> KrillResult { - self.store + fn read_content_cache(&self) -> Option> { + self.cache .read() .unwrap() - .get(&self.key)? - .ok_or(Error::RepositoryServerNotInitialized) + .clone() + } + + fn update_content_cache(&self, content: Arc) { + self.cache.write().unwrap().replace(content); + } + + fn read_content(&self) -> KrillResult> { + if let Some(content) = self.read_content_cache() { + Ok(content) + } else { + let content: Arc = self.store + .read() + .unwrap() + .get(&self.key)? + .ok_or(Error::RepositoryServerNotInitialized)?; + + self.update_content_cache(content.clone()); + + Ok(content) + } } pub fn list_reply(&self, name: &PublisherHandle) -> KrillResult { From a987adeb194ea533eed8c07020f2830a7581a81d Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 15:05:31 +0200 Subject: [PATCH 10/17] Do not instantiate components if no migration is needed (save cache warmup time) (#606) --- src/upgrades/mod.rs | 52 ++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index 50b87424..99314896 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -7,15 +7,13 @@ use std::{fmt, path::Path, str::FromStr, sync::Arc}; use serde::de::DeserializeOwned; use crate::commons::error::KrillIoError; -use crate::constants::KRILL_VERSION; -use crate::daemon::ca::CertAuth; -use crate::pubd::RepositoryAccess; +use crate::commons::util::file; use crate::{commons::api::Handle, daemon::config::Config}; use crate::{ commons::{ crypto::KrillSigner, eventsourcing::{ - AggregateStore, AggregateStoreError, CommandKey, KeyStoreKey, KeyStoreVersion, KeyValueError, KeyValueStore, + AggregateStoreError, CommandKey, KeyStoreKey, KeyStoreVersion, KeyValueError, KeyValueStore, }, }, pubd::RepositoryManager, @@ -170,45 +168,41 @@ pub fn pre_start_upgrade(config: Arc) -> Result<(), UpgradeError> { pub async fn update_storage_version(work_dir: &Path) -> Result<(), UpgradeError> { let current = KeyStoreVersion::current(); - let mut ca_dir = work_dir.to_path_buf(); - ca_dir.push("cas"); - if ca_dir.exists() { - let ca_store: AggregateStore = AggregateStore::disk(work_dir, "cas")?; - if ca_store.get_version()? != current { - ca_store.set_version(¤t)?; - } + if needs_v0_9_0_upgrade(work_dir, "cas") { + debug!("Updating version file for cas"); + file::save_json(¤t, &work_dir.join("cas/version"))?; + } + + if needs_v0_9_0_upgrade(work_dir, "pubd") { + debug!("Updating version file for pubd"); + file::save_json(¤t, &work_dir.join("pubd/version"))?; } - let mut pubd_dir = work_dir.to_path_buf(); - pubd_dir.push("pubd"); - if pubd_dir.exists() { - let pubd_store: AggregateStore = AggregateStore::disk(work_dir, "pubd")?; - if pubd_store.get_version()? != current { - pubd_store.set_version(¤t)?; - } - } - - info!("Upgraded Krill to version: {}", KRILL_VERSION); Ok(()) } fn upgrade_0_9_0(config: Arc) -> Result<(), UpgradeError> { - let mut pubd_dir = config.data_dir.clone(); - pubd_dir.push("pubd"); - if pubd_dir.exists() { + let work_dir = &config.data_dir; + if needs_v0_9_0_upgrade(work_dir, "pubd") { PubdObjectsMigration::migrate(config.clone())?; } - let signer = Arc::new(KrillSigner::build(&config.data_dir)?); - let repo_manager = RepositoryManager::build(config.clone(), signer)?; - let mut cas_dir = config.data_dir.clone(); - cas_dir.push("cas"); - if cas_dir.exists() { + if needs_v0_9_0_upgrade(work_dir, "cas") { + let signer = Arc::new(KrillSigner::build(work_dir)?); + let repo_manager = RepositoryManager::build(config.clone(), signer)?; + CaObjectsMigration::migrate(config, repo_manager)?; } + Ok(()) } +fn needs_v0_9_0_upgrade(work_dir: &Path, ns: &str) -> bool { + let version_path = work_dir.join(ns).join("version"); + let version_found = file::load_json(&version_path).unwrap_or(KeyStoreVersion::Pre0_6); + version_found < KeyStoreVersion::V0_9_0 +} + //------------ Tests --------------------------------------------------------- #[cfg(test)] From e0b7faad587f03f979a9085579c36381be1bf234 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 15:48:07 +0200 Subject: [PATCH 11/17] Skip upgrades if the corresponding keystore dir is empty (e.g. fresh install). --- src/upgrades/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index 99314896..39587b45 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -198,9 +198,15 @@ fn upgrade_0_9_0(config: Arc) -> Result<(), UpgradeError> { } fn needs_v0_9_0_upgrade(work_dir: &Path, ns: &str) -> bool { - let version_path = work_dir.join(ns).join("version"); - let version_found = file::load_json(&version_path).unwrap_or(KeyStoreVersion::Pre0_6); - version_found < KeyStoreVersion::V0_9_0 + let keystore_path = work_dir.join(ns); + if keystore_path.exists() { + let version_path = keystore_path.join("version"); + let version_found = file::load_json(&version_path).unwrap_or(KeyStoreVersion::Pre0_6); + version_found < KeyStoreVersion::V0_9_0 + } else { + false + } + } //------------ Tests --------------------------------------------------------- From 58c84b224f35830a94ec80df0bd67664f072b5bc Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 15:48:52 +0200 Subject: [PATCH 12/17] Update version and changelog for target 0.9.1-rc1 release. --- Cargo.lock | 4 +++- Cargo.toml | 2 +- Changelog.md | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c76e239a..46341c9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -1112,7 +1114,7 @@ dependencies = [ [[package]] name = "krill" -version = "0.9.0" +version = "0.9.1-rc1" dependencies = [ "base64 0.13.0", "basic-cookies", diff --git a/Cargo.toml b/Cargo.toml index ceffedb3..a3efe29e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] # Note: some of these values are also used when building Debian packages below. name = "krill" -version = "0.9.0" +version = "0.9.1-rc1" edition = "2018" authors = [ "The NLnet Labs RPKI team " ] description = "Resource Public Key Infrastructure (RPKI) daemon" diff --git a/Changelog.md b/Changelog.md index ffdfb094..e09149f7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,16 @@ # Change Log +## 0.9.1-RC1 'All for One' + +Note: this is an RC release meant for testing. If no issues are found we will release it +on Monday 19 July 2021. + +This release fixes an issue where the Publication Server would lock up (#606). Users who do +not use Krill to operate their own Publication Server do not need to upgrade to this release. + +This locking issue was cause by slow deserialisation of the repository content. It primarily +affected large repositories because more content makes this process slower, and having more +publishers who publish regularly means it is triggered more frequently. ## 0.9.0 'One for All' From ace22dd7613d465a50e9e756661373dae54dc26e Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Thu, 15 Jul 2021 16:01:32 +0200 Subject: [PATCH 13/17] Deserialize first, then create the Arc. (Doing this in one go was disliked by --no-default-features it seems) --- src/pubd/repository.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pubd/repository.rs b/src/pubd/repository.rs index d7e29522..18f7aaa0 100644 --- a/src/pubd/repository.rs +++ b/src/pubd/repository.rs @@ -199,12 +199,14 @@ impl RepositoryContentProxy { if let Some(content) = self.read_content_cache() { Ok(content) } else { - let content: Arc = self.store + let content: RepositoryContent = self.store .read() .unwrap() .get(&self.key)? .ok_or(Error::RepositoryServerNotInitialized)?; + let content = Arc::new(content); + self.update_content_cache(content.clone()); Ok(content) From 9948183428e3bd9c625e5138aa154a3083288c85 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Fri, 16 Jul 2021 09:43:52 +0200 Subject: [PATCH 14/17] Do not use an Arc - and use a closure for reading the cache to avoid cloning. --- src/pubd/repository.rs | 118 ++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 60 deletions(-) diff --git a/src/pubd/repository.rs b/src/pubd/repository.rs index 18f7aaa0..a5a8c8f9 100644 --- a/src/pubd/repository.rs +++ b/src/pubd/repository.rs @@ -49,7 +49,7 @@ use super::RepositoryAccessInitDetails; /// so that callers don't need to worry about storage details. #[derive(Debug)] pub struct RepositoryContentProxy { - cache: RwLock>>, + cache: RwLock>, store: RwLock, key: KeyStoreKey, } @@ -63,15 +63,27 @@ impl RepositoryContentProxy { let cache = RwLock::new(None); let proxy = RepositoryContentProxy { cache, store, key }; - - // Warm the cache, ignore error indicating there is no content. - info!("Warming the repository content cache, this can take a minute for large repositories."); - let _ = proxy.read_content(); - + proxy.warm_cache()?; + Ok(proxy) } + + fn warm_cache(&self) -> KrillResult<()> { - // Initialize + let key_store_read = self.store + .read() + .unwrap(); + + if key_store_read.has(&self.key)? { + info!("Warming the repository content cache, this can take a minute for large repositories."); + let content = key_store_read.get(&self.key)?.unwrap(); + self.cache.write().unwrap().replace(content); + } + + Ok(()) + } + + /// Initialize pub fn init(&self, work_dir: &Path, uris: PublicationServerUris) -> KrillResult<()> { if self.store.read().unwrap().has(&self.key)? { Err(Error::RepositoryServerAlreadyInitialized) @@ -100,7 +112,7 @@ impl RepositoryContentProxy { // Store newly initialized repo content in cache let mut cache = self.cache.write().unwrap(); - cache.replace(Arc::new(repository_content)); + cache.replace(repository_content); Ok(()) } @@ -119,22 +131,23 @@ impl RepositoryContentProxy { Ok(()) } + /// Return the repository content stats pub fn stats(&self) -> KrillResult { - self.read_content().map(|c| c.stats().clone()) + self.read(|content| Ok(content.stats().clone())) } - // Adds a publisher with an empty set of published objects. - // Replaces an existing publisher if it existed. - // This is only supposed to be called if adding the publisher - // to the RepositoryAccess was successful (and *that* will fail if - // the publisher is a duplicate). This method can only fail if - // there is an issue with the underlying key value store. + /// Add a publisher with an empty set of published objects. + /// + /// Replaces an existing publisher if it existed. + /// This is only supposed to be called if adding the publisher + /// to the RepositoryAccess was successful (and *that* will fail if + /// the publisher is a duplicate). This method can only fail if + /// there is an issue with the underlying key value store. pub fn add_publisher(&self, name: PublisherHandle) -> KrillResult<()> { self.write(|content| content.add_publisher(name)) } - // Removes a publisher and its content. Will also write the updated - // RRDP and rsync content. + /// Removes a publisher and its content. pub fn remove_publisher( &self, name: &PublisherHandle, @@ -144,9 +157,10 @@ impl RepositoryContentProxy { self.write(|content| content.remove_publisher(name, jail, config)) } - // Publish an update for a publisher. Assumes that the RFC 8181 CMS has - // been verified, but will check that all objects are within the publisher's - // uri space (jail). + /// Publish an update for a publisher. + /// + /// Assumes that the RFC 8181 CMS has been verified, but will check that all objects + /// are within the publisher's uri space (jail). pub fn publish( &self, name: &PublisherHandle, @@ -157,17 +171,27 @@ impl RepositoryContentProxy { self.write(|content| content.publish(name, delta.into(), jail, config)) } - // Write all current files to disk + /// Write all current files to disk pub fn write_repository(&self, config: &RepositoryRetentionConfig) -> KrillResult<()> { - self.read_content()?.write_repository(config) + self.read(|content| content.write_repository(config)) } - // Reset the RRDP session + /// Reset the RRDP session pub fn session_reset(&self, config: &RepositoryRetentionConfig) -> KrillResult<()> { self.write(|content| content.session_reset(config)) } + /// Create a list reply containing all current objects for a publisher + pub fn list_reply(&self, name: &PublisherHandle) -> KrillResult { + self.read(|content| content.list_reply(name)) + } + // Get all current objects for a publisher + pub fn current_objects(&self, name: &PublisherHandle) -> KrillResult { + self.read(|content| content.objects_for_publisher(name).map(|o| o.clone())) + } + + // Execute a closure on a mutable repository content in a single write 'transaction' fn write KrillResult<()>>(&self, op: F) -> KrillResult<()> { // If there is any existing content, then we can assume that the cache // has it - because it's initialized when we read the content during @@ -175,8 +199,7 @@ impl RepositoryContentProxy { let store = self.store.write().unwrap(); let mut cache = self.cache.write().unwrap(); - let content_arc: &mut Arc = cache.as_mut().ok_or(Error::RepositoryServerNotInitialized)?; - let content = Arc::::get_mut(content_arc).unwrap(); + let content: &mut RepositoryContent = cache.as_mut().ok_or(Error::RepositoryServerNotInitialized)?; op(content)?; @@ -184,41 +207,16 @@ impl RepositoryContentProxy { Ok(()) } - fn read_content_cache(&self) -> Option> { - self.cache - .read() - .unwrap() - .clone() - } - - fn update_content_cache(&self, content: Arc) { - self.cache.write().unwrap().replace(content); - } - - fn read_content(&self) -> KrillResult> { - if let Some(content) = self.read_content_cache() { - Ok(content) - } else { - let content: RepositoryContent = self.store - .read() - .unwrap() - .get(&self.key)? - .ok_or(Error::RepositoryServerNotInitialized)?; - - let content = Arc::new(content); - - self.update_content_cache(content.clone()); - - Ok(content) - } - } - - pub fn list_reply(&self, name: &PublisherHandle) -> KrillResult { - self.read_content()?.list_reply(name) - } - - pub fn current_objects(&self, name: &PublisherHandle) -> KrillResult { - self.read_content()?.objects_for_publisher(name).map(|o| o.clone()) + // Execute a closure on a mutable repository content in a single read 'transaction' + // + // This function fails if the repository content is not initialized. + fn read KrillResult>(&self, op: F) -> KrillResult { + // Note that because the content is initialized it is implied that the cache MUST always be + // set. I.e. it is set on initialization and updated whenever the repository content is updated. + // So, we can safely read from the cache only. + let cache = self.cache.read().unwrap(); + let content = cache.as_ref().ok_or(Error::RepositoryServerNotInitialized)?; + op(content) } } From 6119d3bb42bccbac902b8af03f3713ab54e382b5 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Fri, 16 Jul 2021 09:48:51 +0200 Subject: [PATCH 15/17] Also clear cache when clearing the repository content. --- src/pubd/repository.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pubd/repository.rs b/src/pubd/repository.rs index a5a8c8f9..463b85ab 100644 --- a/src/pubd/repository.rs +++ b/src/pubd/repository.rs @@ -128,6 +128,9 @@ impl RepositoryContentProxy { store.drop_key(&self.key)?; } + let mut cache = self.cache.write().unwrap(); + cache.take(); + Ok(()) } From 8e06582150687788903f9eef2d7c8b9f71e7b154 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Fri, 16 Jul 2021 09:59:55 +0200 Subject: [PATCH 16/17] Remove async. --- src/daemon/http/server.rs | 2 +- src/daemon/krillserver.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index c818d242..8b26e430 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -1146,7 +1146,7 @@ pub async fn api_ca_init(req: Request) -> RoutingResult { let state = req.state().clone(); match req.json().await { - Ok(ca_init) => render_empty_res(state.ca_init(ca_init).await), + Ok(ca_init) => render_empty_res(state.ca_init(ca_init)), Err(e) => render_error(e), } }) diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 5153aa80..962c1eb6 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -604,7 +604,7 @@ impl KrillServer { self.ca_manager.get_ca(handle).await.map(|ca| ca.publisher_request()) } - pub async fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { + pub fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { let handle = init.unpack(); self.ca_manager.init_ca(&handle) } From 39629ff56bb05b9e67eb0e71cca18651fdf62261 Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Fri, 16 Jul 2021 10:07:43 +0200 Subject: [PATCH 17/17] Remove assignments which are no longer useful now the RwLock is removed. --- src/daemon/http/server.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 8b26e430..37a7f4e2 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -334,7 +334,6 @@ pub async fn health(req: Request) -> RoutingResult { pub async fn metrics(req: Request) -> RoutingResult { if req.is_get() && req.path().segment().starts_with("metrics") { let server = req.state(); - let server = server; struct AllBgpStats { announcements_valid: HashMap, @@ -595,8 +594,7 @@ pub async fn rfc8181(req: Request) -> RoutingResult { Err(e) => return render_error(e), }; - let read = state; - match read.rfc8181(publisher, bytes) { + match state.rfc8181(publisher, bytes) { Ok(bytes) => Ok(HttpResponse::rfc8181(bytes.to_vec())), Err(e) => render_error(e), }