From 22de300fa8dd664a119a80dfe73b6e86946d714a Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Wed, 2 Nov 2022 11:44:16 +0100 Subject: [PATCH] Restructure TAL configuration and remove init command. (#796) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit restructures the TAL configuration in response to the dropped requirement to opt into the ARIN TAL. Routinator will now use the bundled RIR TALs directly unless told otherwise by the new --no-rir-tals command line and config option. The additional bundled TALs can be added via the new --tal command line and config option. Additionally, the TAL directory can still be used via the --extra-tals-dir option. The tal-dir option has been removed but will still be accepted – and ignored – in the config file only. The init command has been removed. Co-authored-by: Alex Band Co-authored-by: ximon18 <3304436+ximon18@users.noreply.github.com> Co-authored-by: Luuk Hendriks --- Cargo.toml | 10 +- Dockerfile | 11 +- doc/manual/source/building.rst | 21 +- doc/manual/source/configuration.rst | 283 ++++++++++++++++++++----- doc/manual/source/index.rst | 1 - doc/manual/source/initialisation.rst | 234 --------------------- doc/manual/source/installation.rst | 150 +++++--------- doc/manual/source/manual-page.rst | 138 +++++-------- etc/routinator.conf.example | 12 +- etc/routinator.conf.system-service | 9 +- pkg/common/routinator-init | 21 -- pkg/common/service.preset | 1 - pkg/rpm/scriptlets.toml | 3 +- pkg/rules/packages-to-test.yml | 7 - pkg/test-scripts/test-routinator.sh | 65 +++--- src/collector/base.rs | 24 +-- src/config.rs | 171 +++++++++------- src/engine.rs | 137 +++++++------ src/operation.rs | 295 +-------------------------- src/tals.rs | 123 +++++++---- tals/README.md | 13 +- 21 files changed, 654 insertions(+), 1075 deletions(-) delete mode 100644 doc/manual/source/initialisation.rst delete mode 100755 pkg/common/routinator-init delete mode 100644 pkg/common/service.preset diff --git a/Cargo.toml b/Cargo.toml index f4bc032..509a11e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,15 +85,13 @@ assets = [ ["target/release/routinator", "usr/bin/", "755"], ["README.md", "usr/share/doc/routinator/", "644"], ["doc/routinator.1", "usr/share/man/man1/routinator.1", "644"], - ["etc/routinator.conf.system-service", "etc/routinator/routinator.conf", "644"], - ["pkg/common/service.preset", "/lib/systemd/system-preset/50-routinator.preset", "644"], - ["pkg/common/routinator-init", "usr/bin/", "755"] + ["etc/routinator.conf.system-service", "etc/routinator/routinator.conf", "644"] ] maintainer-scripts = "pkg/debian" changelog = "target/debian/changelog" # this will be generated by the pkg workflow copyright = "Copyright (c) 2020, NLnet Labs. All rights reserved." conf-files = ["/etc/routinator/routinator.conf"] -systemd-units = { unit-name = "routinator", unit-scripts = "pkg/common", enable = false } +systemd-units = { unit-name = "routinator", unit-scripts = "pkg/common", enable = true } [package.metadata.deb.variants.minimal] @@ -113,9 +111,7 @@ assets = [ { source = "target/release/routinator", dest = "/usr/bin/routinator", mode = "755" }, { source = "target/rpm/routinator.service", dest = "/lib/systemd/system/routinator.service", mode = "644" }, { source = "doc/routinator.1", dest = "/usr/share/man/man1/routinator.1", mode = "644", doc = true }, - { source = "etc/routinator.conf.system-service", dest = "/etc/routinator/routinator.conf", mode = "644", config = true }, - { source = "pkg/common/routinator-init", dest = "/usr/bin/routinator-init", mode = "755" }, - { source = "pkg/common/service.preset", dest = "/lib/systemd/system-preset/50-routinator.preset", mode = "644" }, + { source = "etc/routinator.conf.system-service", dest = "/etc/routinator/routinator.conf", mode = "644", config = true } ] # These get set using cargo-generate-rpm --set-metadata at package build time. #post_install_script = ... diff --git a/Dockerfile b/Dockerfile index 94ab2b5..daa93cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -152,16 +152,10 @@ RUN apk add --no-cache libgcc rsync tini RUN addgroup -g ${RUN_USER_GID} ${RUN_USER} && \ adduser -D -u ${RUN_USER_UID} -G ${RUN_USER} ${RUN_USER} -# Create the repository and TAL directories -RUN mkdir -p /home/${RUN_USER}/.rpki-cache/repository /home/${RUN_USER}/.rpki-cache/tals && \ +# Create the repository directory +RUN mkdir -p /home/${RUN_USER}/.rpki-cache/repository && \ chown -R ${RUN_USER_UID}:${RUN_USER_GID} /usr/local/bin/routinator /home/${RUN_USER}/.rpki-cache -# Due to ARIN TAL distribution terms, we can't do this here. -# An individual user, however, might want to anyway - after reviewing -# https://www.arin.net/resources/rpki/tal.html. -# -#COPY --from=build /tmp/routinator/tals/*.tal /home/${RUN_USER}/.rpki-cache/tals/ - # Switch to our applications user USER $RUN_USER_UID @@ -175,4 +169,3 @@ EXPOSE 9556/tcp # way of activating Tini, but cannot be enabled from inside the Docker image). ENTRYPOINT ["/sbin/tini", "--", "routinator"] CMD ["server", "--rtr", "0.0.0.0:3323", "--http", "0.0.0.0:9556"] - diff --git a/doc/manual/source/building.rst b/doc/manual/source/building.rst index 74e9566..b075c62 100644 --- a/doc/manual/source/building.rst +++ b/doc/manual/source/building.rst @@ -89,9 +89,6 @@ The command will build Routinator and install it in the same directory that Cargo itself lives in, likely ``$HOME/.cargo/bin``. This means Routinator will be in your path, too. -.. Note:: Before you can use Routinator, you must first - :doc:`initialise` the application. - Updating """""""" @@ -348,15 +345,7 @@ run Routinator on Security Enhanced Linux (SELinux) using CentOS 7. Edit the PATH line to include "/home/routinator/.cargo/bin" PATH=$PATH:$HOME/.local/bin:$HOME/bin:/home/routinator/.cargo/bin -14. Initialise Routinator, accept the ARIN TAL and exit back to the user with - :command:`sudo`: - -.. code-block:: bash - - /home/routinator/.cargo/bin/routinator -b /opt/routinator init -f --accept-arin-rpa - exit - -15. Create a routinator systemd script using the template below: +14. Create a routinator systemd script using the template below: .. code-block:: bash @@ -384,7 +373,7 @@ run Routinator on Security Enhanced Linux (SELinux) using CentOS 7. /home/routinator/.cargo/bin/routinator -v -b /opt/routinator server \ --http 127.0.0.1:8080 --rtr 172.16.47.235:8323 --rtr [2001:db8::43]:8323 -16. Configure SELinux to allow connections to localhost and to allow +15. Configure SELinux to allow connections to localhost and to allow :program:`rsync` to write to the ``/opt/routinator`` directory: .. code-block:: bash @@ -392,7 +381,7 @@ run Routinator on Security Enhanced Linux (SELinux) using CentOS 7. sudo setsebool -P httpd_can_network_connect 1 sudo semanage permissive -a rsync_t -17. Reload the systemd daemon and set the routinator service to start at +16. Reload the systemd daemon and set the routinator service to start at boot: .. code-block:: bash @@ -401,7 +390,7 @@ run Routinator on Security Enhanced Linux (SELinux) using CentOS 7. sudo systemctl enable routinator.service sudo systemctl start routinator.service -18. Set up the firewall to permit :program:`ssh`, HTTPS and port 8323 for the +17. Set up the firewall to permit :program:`ssh`, HTTPS and port 8323 for the RTR protocol: .. code-block:: bash @@ -421,6 +410,6 @@ run Routinator on Security Enhanced Linux (SELinux) using CentOS 7. source address="" port port=8323 protocol=tcp accept' sudo firewall-cmd --reload -19. Navigate to :samp:`https://{}/metrics` to see if it's +18. Navigate to :samp:`https://{}/metrics` to see if it's working. You should authenticate with the username and password that you provided in step 10 of setting up the RPKI Validation Server. diff --git a/doc/manual/source/configuration.rst b/doc/manual/source/configuration.rst index 88cd5f4..9af19eb 100644 --- a/doc/manual/source/configuration.rst +++ b/doc/manual/source/configuration.rst @@ -1,3 +1,11 @@ +.. Important:: **With Routinator 0.12.0 and newer, initialisation to accept + the ARIN Relying Party Agreement (RPA) is no longer + required.** The RPA `has been updated + `_ to allow the + ARIN TAL to be embedded in Relying Party software. By + default, Routinator is now set up to fetch and validate all + RPKI data needed for production environments. + Configuration ============= @@ -13,22 +21,20 @@ the default values can be found in the `repository `_. Routinator can run as a daemon but you can also use it interactively from the -command line. However, there are several considerations with regards to how -you've installed and how you intend to use Routinator, which we'll cover -below. +command line. There are several considerations with regards to how you've +installed and how you intend to use Routinator, which we'll cover below. -Routinator Installed From a Package +Setup When Installed From a Package ----------------------------------- -As explained in the :doc:`initialisation` section, the installation script -will run as the user *routinator* and refer to the configuration file -:file:`/etc/routinator/routinator.conf` which contains the following -pre-configured options: +The installation script will set up Routinator to run as the user +*routinator* and be configured to start at boot. Routinator will use the +configuration file :file:`/etc/routinator/routinator.conf` which contains the +following pre-configured options: .. code-block:: toml repository-dir = "/var/lib/routinator/rpki-cache" - tal-dir = "/var/lib/routinator/tals" rtr-listen = ["127.0.0.1:3323"] http-listen = ["127.0.0.1:8323"] @@ -57,7 +63,7 @@ make changes. :doc:`user interface` or one of the :doc:`HTTP endpoints`. -Routinator Built with Cargo +Setup When Built with Cargo --------------------------- If you have built Routinator using Cargo, you have made your own decisions @@ -65,9 +71,16 @@ with regards to the user that it runs as and the privileges it has. There is no default configuration file, as it is your choice if you want to use one. If you run Routinator without referring to a configuration file it will check -if there is a :file:`$HOME/.routinator.conf` file and if it exists, use it. +if the file :file:`$HOME/.routinator.conf` exists and if it does, use it. If no configuration file is available, the default values are used. +You can specify the location of the RPKI cache directory using the +:option:`--repository-dir` option. If you don't, one will be created in the +default location :file:`$HOME/.rpki-cache/repository`. The :doc:`HTTP +service` and :doc:`RTR service` must be started +explicitly using the command line options :option:`--http` and +:option:`--rtr`, respectively, or via the configuration file. + You can view the default settings Routinator runs with using: .. code-block:: text @@ -81,37 +94,124 @@ own: .. code-block:: toml - allow-dubious-hosts = false - dirty = false - disable-rrdp = false - disable-rsync = false - exceptions = [] - expire = 7200 - history-size = 10 - http-listen = [] - log = "default" - log-level = "WARN" - max-object-size = 20000000 - refresh = 600 - repository-dir = "/Users/routinator/.rpki-cache/repository" - retry = 600 - rrdp-fallback-time = 3600 - rrdp-proxies = [] - rrdp-root-certs = [] - rsync-command = "rsync" - rsync-timeout = 300 - rtr-client-metrics = false - rtr-listen = [] - rtr-tcp-keepalive = 60 - stale = "reject" - strict = false - syslog-facility = "daemon" - systemd-listen = false - tal-dir = "/Users/routinator/.rpki-cache/tals" - unknown-objects = "warn" - unsafe-vrps = "warn" - validation-threads = 4 + allow-dubious-hosts = false + dirty = false + disable-rrdp = false + disable-rsync = false + enable-bgpsec = false + exceptions = [] + expire = 7200 + history-size = 10 + http-listen = [] + http-tls-listen = [] + log = "default" + log-level = "WARN" + max-ca-depth = 32 + max-object-size = 20000000 + refresh = 600 + repository-dir = "/Users/routinator/.rpki-cache/repository" + retry = 600 + rrdp-fallback-time = 3600 + rrdp-max-delta-count = 100 + rrdp-proxies = [] + rrdp-root-certs = [] + rrdp-timeout = 300 + rsync-command = "rsync" + rsync-timeout = 300 + rtr-client-metrics = false + rtr-listen = [] + rtr-tcp-keepalive = 60 + rtr-tls-listen = [] + stale = "reject" + strict = false + syslog-facility = "daemon" + systemd-listen = false + unknown-objects = "warn" + unsafe-vrps = "accept" + validation-threads = 10 + +Trust Anchor Locators +--------------------- + +Fetching data is done by connecting to the :term:`Trust Anchor Locators +(TALs) ` of the five Regional Internet Registries +(RIRs): AFRINIC, APNIC, ARIN, LACNIC and RIPE NCC. TALs provide hints for +the trust anchor certificates to be used both to discover and validate all +RPKI content. **By default, Routinator will be set up for use in production +environments and run with the production TALs of the five RIRs.** + +Some RIRs and third parties also provide separate TALs for testing purposes, +allowing operators to gain experience with using RPKI in a safe environment. +Both the production and testbed TALs are bundled with Routinator and can be +enabled and disabled using command line and configuration file options. + +Run the following command to list all available TALs: + +.. code-block:: text + + routinator --tal=list +This displays the following overview: + +.. code-block:: text + + .---- RIR TALs + | .- RIR test TALs + V V + + X afrinic AFRINIC production TAL + X apnic APNIC production TAL + X arin ARIN production TAL + X lacnic LACNIC production TAL + X ripe RIPE production TAL + X apnic-testbed APNIC RPKI Testbed + X arin-ote ARIN Operational Test and Evaluation Environment + X ripe-pilot RIPE NCC RPKI Test Environment + nlnetlabs-testbed NLnet Labs RPKI Testbed + +You can influence which TALs Routinator uses with the :option:`--tal` option, +which can be combined with the :option:`--no-rir-tals` option to leave out +all RIR production TALs, as well as the :option:`--extra-tals-dir` option to +specify a directory containing extra TALs to use. + +For example, if you want to add the RIPE NCC RPKI Test Environment to the +default TAL set, run: + +.. code-block:: text + + routinator --tal=ripe-pilot + +If you want to run Routinator without any of the production TALs and only +fetch data from the ARIN Operational Test and Evaluation Environment, run: + +.. code-block:: text + + routinator --no-rir-tals --tal=arin-ote + +Lastly, if you would like to use a TAL that isn't bundled with Routinator you +can place it in a directory of your choice, for example +:file:`/var/lib/routinator/tals`, and refer to it by running: + +.. code-block:: text + + routinator --extra-tals-dir="/var/lib/routinator/tals" + +Routinator will use all files in this directory with an extension of *.tal* +as TALs. These files need to be in the format described by :rfc:`8630`. Note +that Routinator will use all TALs provided. That means that if a TAL in this +directory is one of the bundled TALs, then these resources will be validated +twice. + +.. versionadded:: 0.9.0 + :option:`--list-tals`, :option:`--rir-tals`, :option:`--rir-test-tals`, + :option:`--tal` and :option:`--skip-tal` +.. deprecated:: 0.9.0 + ``--decline-arin-rpa``, use :option:`--skip-tal` instead +.. versionadded:: 0.12.0 + :option:`--extra-tals-dir` +.. deprecated:: 0.12.0 + The ``init`` subcommand, :option:`--list-tals` + Using Tmpfs for the RPKI Cache ------------------------------ @@ -121,12 +221,6 @@ undesirable in your setup, you can choose to store the cache in volatile memory using the `tmpfs file system `_. -When setting this up, you should make sure to only put the directory for the -local RPKI cache in *tmpfs* and not the directory where the Trust Anchor -Locators reside. Both locations are set in the :ref:`configuration file -` with the ``repository-dir`` and ``tal-dir`` -options, respectively. - If you have installed Routinator using a package, by default the RPKI cache directory will be :file:`/var/lib/routinator/rpki-cache`, so we'll use that as an example. Note that the directory you choose must exist before the mount @@ -145,8 +239,99 @@ served to your routers. Also keep in mind that every time you restart the machine, the contents of the *tmpfs* file system will be lost. This means that Routinator will have to rebuild its cache from scratch. This is not a problem, other than it having -to download several gigabytes of data, which usually takes about ten minutes -to complete. During this time all services will be unavailable. +to download several hundred megabytes of data, which usually takes about ten +minutes to complete. During this time all services will be unavailable. Note that your routers should be configured to have a secondary relying party instance available at all times. + +Verifying Configuration +----------------------- + +You should verify if Routinator has been configured correctly and your +firewall allows the required outbound connections on ports 443 and 873. From +a cold start, it will take ten to fifteen minutes to do the first validation +run that builds up the validated cache. Subsequent runs will be much faster, +because only the changes between the repositories and the validated cache +need to be processed. + +If you have installed Routinator from a package and run it as a service, you +can check the status using: + +.. code-block:: bash + + sudo systemctl status routinator + +And check the logs using: + +.. code-block:: bash + + sudo journalctl --unit=routinator + +.. Important:: Because it is expected that the state of the entire RPKI is not + perfect at all times, you may see several warnings about objects + that are either stale or failed cryptographic verification, or + repositories that are temporarily unavailable. + +If you have built Routinator using Cargo it is recommended to perform an +initial test run. You can do this by having Routinator print a validated ROA +payload (VRP) list with the :subcmd:`vrps` subcommand, and using :option:`-v` +twice to increase the :doc:`log level` to *debug*: + +.. code-block:: bash + + routinator -vv vrps + +Now, you can see how Routinator connects to the RPKI trust anchors, downloads +the the contents of the repositories to your machine, verifies it and +produces a list of VRPs in the default CSV format to standard output. + +.. code-block:: text + + [INFO] Using the following TALs: + [INFO] * afrinic + [INFO] * apnic + [INFO] * arin + [INFO] * lacnic + [INFO] * ripe + [DEBUG] Found valid trust anchor https://rpki.ripe.net/ta/ripe-ncc-ta.cer. Processing. + [DEBUG] Found valid trust anchor https://rrdp.lacnic.net/ta/rta-lacnic-rpki.cer. Processing. + [DEBUG] Found valid trust anchor https://rpki.afrinic.net/repository/AfriNIC.cer. Processing. + [DEBUG] Found valid trust anchor https://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer. Processing. + [DEBUG] Found valid trust anchor https://rrdp.arin.net/arin-rpki-ta.cer. Processing. + [DEBUG] RRDP https://rrdp.ripe.net/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.lacnic.net/rrdp/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.apnic.net/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.afrinic.net/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.arin.net/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.apnic.net/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rpki-rrdp.us-east-2.amazonaws.com/rrdp/08c2f264-23f9-49fb-9d43-f8b50bec9261/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rpki-rrdp.us-east-2.amazonaws.com/rrdp/08c2f264-23f9-49fb-9d43-f8b50bec9261/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rpki.akrn.net/rrdp/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rpki.akrn.net/rrdp/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rpki.admin.freerangecloud.com/rrdp/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rpki.admin.freerangecloud.com/rrdp/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rpki.cnnic.cn/rrdp/notify.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.ripe.net/notification.xml: snapshot update completed. + [DEBUG] RRDP https://0.sb/rrdp/notification.xml: updating from snapshot. + [DEBUG] RRDP https://0.sb/rrdp/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rrdp.sub.apnic.net/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rrdp.sub.apnic.net/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rpki.roa.net/rrdp/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rpki.roa.net/rrdp/notification.xml: snapshot update completed. + [DEBUG] RRDP https://rrdp.rp.ki/notification.xml: updating from snapshot. + [DEBUG] RRDP https://rpki.cnnic.cn/rrdp/notify.xml: snapshot update completed. + ... + ASN,IP Prefix,Max Length,Trust Anchor + AS137884,103.116.116.0/23,23,apnic + AS9003,91.151.112.0/20,20,ripe + AS38553,120.72.19.0/24,24,apnic + AS58045,37.209.242.0/24,24,ripe + AS9583,202.177.175.0/24,24,apnic + AS50629,2a0f:ba80::/29,29,ripe + AS398085,2602:801:a008::/48,48,arin + AS21050,83.96.22.0/24,24,ripe + AS55577,183.82.223.0/24,24,apnic + AS44444,157.167.73.0/24,24,ripe + AS197695,194.67.97.0/24,24,ripe + ... \ No newline at end of file diff --git a/doc/manual/source/index.rst b/doc/manual/source/index.rst index ba69517..e938f9b 100644 --- a/doc/manual/source/index.rst +++ b/doc/manual/source/index.rst @@ -59,7 +59,6 @@ Open-source with professional support services installation building - initialisation configuration .. toctree:: diff --git a/doc/manual/source/initialisation.rst b/doc/manual/source/initialisation.rst deleted file mode 100644 index 7623b9a..0000000 --- a/doc/manual/source/initialisation.rst +++ /dev/null @@ -1,234 +0,0 @@ -Initialisation -============== - -Before running Routinator for the first time, you must prepare the working -environment. You do this using the :subcmd:`init` subcommand. This will create -the directory for the :term:`Trust Anchor Locator (TAL)` files and copy the -desired TALs into it, and create the directory for the local RPKI cache. - -If you have installed Routinator using a package from our software package -repository, the application is configured to run as a system service with the -user *routinator*. We have included an initialisation script named -:program:`routinator-init` and pre-installed a :doc:`configuration -file` located in ``/etc/routinator/routinator.conf`` to make the -setup process easy for you. The configuration is meant to prepare Routinator for -production environments, explicitly setting the TAL and RPKI cache directories -and enabling the HTTP and RTR servers on localhost. - -The :program:`routinator-init` script invokes the :subcmd:`init` subcommand as -the user *routinator* and takes configuration file into consideration. All of -the options for the :subcmd:`init` subcommand can be appended to the -:program:`routinator-init` script, which are described below. If you have built -Routinator using Cargo you also have to perform the initialisation steps, but in -this case you invoke the :subcmd:`init` subcommand directly. - -.. Important:: There is a subtle difference in the initialisation commands - depending on how you installed Routinator. - - When installed using a package, you would for example enter: - - .. code-block:: text - - routinator-init --list-tals - - When built using Cargo, you would use: - - .. code-block:: text - - routinator init --list-tals - -Trust Anchor Locators ---------------------- - -Trust Anchor Locators (TALs) provide hints for the trust anchor certificates to -be used both to discover and validate all RPKI content. There are five TALs, one -for each Regional Internet Registry (RIR). For production environments these are -the only five you will ever need to fetch and validate all available RPKI data. - -Some RIRs and third parties also provide separate TALs for testing purposes, -allowing operators to gain experience with using RPKI in a safe environment. -Both the production and testbed TALs are bundled with Routinator and can be -installed with the :subcmd:`init` subcommand. - -To get an overview of all available TALs use the :option:`--list-tals` option: - -.. code-block:: text - - routinator init --list-tals - -This displays the following overview: - -.. code-block:: text - - .---- --rir-tals - | .- --rir-test-tals - V V - - X afrinic AFRINIC production TAL - X apnic APNIC production TAL - X arin ARIN production TAL - X lacnic LACNIC production TAL - X ripe RIPE production TAL - X apnic-testbed APNIC RPKI Testbed - X arin-ote ARIN Operational Test and Evaluation Environment - X ripe-pilot RIPE NCC RPKI Test Environment - nlnetlabs-testbed NLnet Labs RPKI Testbed - -Preparing for Production Environments -""""""""""""""""""""""""""""""""""""" - -.. Warning:: Using the TAL from ARIN requires you to read and accept their - `Relying Party Agreement - `_ before you can - use it. Running the :subcmd:`init` subcommand will provide you with - instructions. - -By default, the repository and TAL directory will be created under -:file:`$HOME/.rpki-cache`. You can change their location using the -:option:`--repository-dir` and :option:`--tal-dir` options, or by using a -:doc:`configuration file`. - -In the most common scenario, you will want to install the TALs of the five RIRs. -To do this, run the following command: - -.. code-block:: text - - routinator init --rir-tals - -This will return the following message: - -.. code-block:: text - - Before we can install the ARIN TAL, you must have read - and agree to the ARIN Relying Party Agreement (RPA). - It is available at - - https://www.arin.net/resources/manage/rpki/rpa.pdf - - If you agree to the RPA, please run the command - again with the --accept-arin-rpa option. - -Running the :subcmd:`init` subcommand with the :option:`--accept-arin-rpa` -option added will create the repository and TAL directory and copy the five -Trust Anchor Locator files into it: - -.. code-block:: bash - - routinator init --rir-tals --accept-arin-rpa - -If you built Routinator using Cargo and set up a :doc:`configuration -file` before initialisation, make sure to refer to it using the -:option:`--config` option, e.g.: - -.. code-block:: bash - - routinator --config /home/routinator/routinator.conf init --rir-tals --accept-arin-rpa - -If you decide you cannot agree to the ARIN RPA terms, you can use the -:option:`--skip-tal` option to exclude the TAL. If, at a later point, you wish -to include the ARIN TAL you can add it to your current installation using the -:option:`--force` option, to force the installation of all TALs. - -Preparing for Test Environments -""""""""""""""""""""""""""""""" - -To install all of the TALs for the various test environments, you can use the -:option:`--rir-test-tals` option. However, in most cases you will want to -install a specific one, using the :option:`--tal` option. - -For example, to add the TAL for the `ARIN Operational Test and Evaluation -Environment `_ to an already -initialised Routinator, enter: - -.. code-block:: bash - - routinator init --force --tal arin-ote - -.. versionadded:: 0.9.0 - :option:`--list-tals`, :option:`--rir-tals`, :option:`--rir-test-tals`, - :option:`--tal` and :option:`--skip-tal` -.. deprecated:: 0.9.0 - ``--decline-arin-rpa``, use :option:`--skip-tal` instead - -Verifying Initialisation ------------------------- - -You should verify if Routinator has been initialised correctly and your firewall -allows the required outbound connections on ports 443 and 873. From a cold -start, it will take ten to fifteen minutes to do the first validation run that -builds up the validated cache. Subsequent runs will be much faster, because only -the changes between the repositories and the validated cache need to be -processed. - -If you have installed Routinator from a package and run it as a service, you can -check the status using: - -.. code-block:: bash - - sudo systemctl status routinator - -And check the logs using: - -.. code-block:: bash - - sudo journalctl --unit=routinator - -.. Important:: Because it is expected that the state of the entire RPKI is not - perfect as all times, you may see several warnings about objects - that are either stale or failed cryptographic verification, or - repositories that are temporarily unavailable. - -If you have built Routinator using Cargo it is recommended to perform an -initial test run. You can do this by having Routinator print a validated ROA -payload (VRP) list with the :subcmd:`vrps` subcommand, and using :option:`-v` -twice to increase the :doc:`log level` to *debug*: - -.. code-block:: bash - - routinator -vv vrps - -Now, you can see how Routinator connects to the RPKI trust anchors, downloads -the the contents of the repositories to your machine, verifies it and produces a -list of VRPs in the default CSV format to standard output. - -.. code-block:: text - - RRDP https://rrdp.ripe.net/notification.xml: Tree has 0 entries. - RRDP https://rrdp.ripe.net/notification.xml: updating from snapshot. - Found valid trust anchor https://rpki.afrinic.net/repository/AfriNIC.cer. Processing. - Found valid trust anchor https://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer. Processing. - RRDP https://rrdp.afrinic.net/notification.xml: Tree has 0 entries. - RRDP https://rrdp.afrinic.net/notification.xml: updating from snapshot. - RRDP https://rrdp.apnic.net/notification.xml: Tree has 0 entries. - RRDP https://rrdp.apnic.net/notification.xml: updating from snapshot. - RRDP https://rrdp.afrinic.net/notification.xml: snapshot update completed. - Found valid trust anchor https://rrdp.arin.net/arin-rpki-ta.cer. Processing. - RRDP https://rrdp.arin.net/notification.xml: Tree has 0 entries. - RRDP https://rrdp.arin.net/notification.xml: updating from snapshot. - rsync://repository.lacnic.net/rpki/: successfully completed. - Found valid trust anchor https://rrdp.lacnic.net/ta/rta-lacnic-rpki.cer. Processing. - RRDP https://rrdp.lacnic.net/rrdp/notification.xml: Tree has 0 entries. - RRDP https://rrdp.lacnic.net/rrdp/notification.xml: updating from snapshot. - RRDP https://rrdp.arin.net/notification.xml: snapshot update completed. - RRDP https://rrdp.sub.apnic.net/notification.xml: Tree has 0 entries. - RRDP https://rrdp.sub.apnic.net/notification.xml: updating from snapshot. - RRDP https://rrdp.ripe.net/notification.xml: snapshot update completed. - RRDP https://rrdp.sub.apnic.net/notification.xml: snapshot update completed. - RRDP https://rpki-repo.registro.br/rrdp/notification.xml: Tree has 0 entries. - RRDP https://rpki-repo.registro.br/rrdp/notification.xml: updating from snapshot. - RRDP https://rrdp.twnic.tw/rrdp/notify.xml: Tree has 0 entries. - RRDP https://rrdp.twnic.tw/rrdp/notify.xml: updating from snapshot. - ... - ASN,IP Prefix,Max Length,Trust Anchor - AS137884,103.116.116.0/23,23,apnic - AS9003,91.151.112.0/20,20,ripe - AS38553,120.72.19.0/24,24,apnic - AS58045,37.209.242.0/24,24,ripe - AS9583,202.177.175.0/24,24,apnic - AS50629,2a0f:ba80::/29,29,ripe - AS398085,2602:801:a008::/48,48,arin - AS21050,83.96.22.0/24,24,ripe - AS55577,183.82.223.0/24,24,apnic - AS44444,157.167.73.0/24,24,ripe - AS197695,194.67.97.0/24,24,ripe - ... \ No newline at end of file diff --git a/doc/manual/source/installation.rst b/doc/manual/source/installation.rst index 5ce74ad..e179a05 100644 --- a/doc/manual/source/installation.rst +++ b/doc/manual/source/installation.rst @@ -8,15 +8,14 @@ Routinator has minimal system requirements. When choosing a system, a powerful CPU is not required. Make sure you have 1GB of available memory and 4GB of disk space for the application. -Please keep in mind that the RPKI consists of a very large number of small -files. As a result, Routinator will use a large number of inodes. You should +Please keep in mind that the RPKI consists of a great number of small files. +As a result, Routinator will use a large amount of inodes. You should accommodate for at least 500,000 inodes, but one million will provide more breathing room. This will give you ample margin for the RPKI repositories to grow over time, as adoption increases. -.. Tip:: A "No space left on device" error may be caused by running out of - inodes instead of disk space. To verify this, the ``df -i`` command - shows the amount of inodes available, used, and free. +.. Tip:: The ``df -i`` command shows the amount of inodes available, used, + and free. As new RPKI repositories can emerge in any IP address range and on any domain name, outbound traffic must not be blocked based on IP or DNS in any way. @@ -93,26 +92,11 @@ to get started. sudo apt install routinator - Before running Routinator for the first time, you must prepare the - directory for the local RPKI cache, as well as the directory where the - :term:`Trust Anchor Locator (TAL)` files reside. After entering this - command, **follow the instructions** provided about the ARIN TAL: - - .. code-block:: bash - - sudo routinator-init - - To learn more about this process refer to the :doc:`initialisation` - section. After successful initialisation you can enable Routinator - with: - - .. code-block:: bash - - sudo systemctl enable --now routinator - - By default, Routinator will start the RTR server on port 3323 and the - HTTP server on port 8323. These, and other values can be changed in - the :doc:`configuration file` located in + After installation Routinator will run immediately as the user + *routinator* and be configured to start at boot. By default, it will + run the RTR server on port 3323 and the HTTP server on port 8323. + These, and other values can be changed in the :doc:`configuration + file` located in :file:`/etc/routinator/routinator.conf`. You can check the status of Routinator with: @@ -181,26 +165,11 @@ to get started. sudo apt install routinator - Before running Routinator for the first time, you must prepare the - directory for the local RPKI cache, as well as the directory where the - :term:`Trust Anchor Locator (TAL)` files reside. After entering this - command, **follow the instructions** provided about the ARIN TAL: - - .. code-block:: bash - - sudo routinator-init - - To learn more about this process refer to the :doc:`initialisation` - section. After successful initialisation you can enable Routinator - with: - - .. code-block:: bash - - sudo systemctl enable --now routinator - - By default, Routinator will start the RTR server on port 3323 and the - HTTP server on port 8323. These, and other values can be changed in - the :doc:`configuration file` located in + After installation Routinator will run immediately as the user + *routinator* and be configured to start at boot. By default, it will + run the RTR server on port 3323 and the HTTP server on port 8323. + These, and other values can be changed in the :doc:`configuration + file` located in :file:`/etc/routinator/routinator.conf`. You can check the status of Routinator with: @@ -243,26 +212,11 @@ to get started. sudo yum install -y routinator - Before running Routinator for the first time, you must prepare the - directory for the local RPKI cache, as well as the directory where the - :term:`Trust Anchor Locator (TAL)` files reside. After entering this - command, **follow the instructions** provided about the ARIN TAL: - - .. code-block:: bash - - sudo routinator-init - - To learn more about this process refer to the :doc:`initialisation` - section. After successful initialisation you can enable Routinator - with: - - .. code-block:: bash - - sudo systemctl enable --now routinator - - By default, Routinator will start the RTR server on port 3323 and the - HTTP server on port 8323. These, and other values can be changed in - the :doc:`configuration file` located in + After installation Routinator will run immediately as the user + *routinator* and be configured to start at boot. By default, it will + run the RTR server on port 3323 and the HTTP server on port 8323. + These, and other values can be changed in the :doc:`configuration + file` located in :file:`/etc/routinator/routinator.conf`. You can check the status of Routinator with: @@ -279,49 +233,48 @@ to get started. .. group-tab:: Docker - Routinator Docker images are built with Alpine Linux for - ``amd64``/``x86_64`` architecture. - - Due to the impracticality of complying with terms and conditions in an - unsupervised Docker environment, it is necessary to first review and - agree to the `ARIN Relying Party Agreement (RPA) - `_. If you agree, you - can let the Routinator Docker image install the :term:`Trust Anchor - Locator (TAL)` files into a mounted volume that is later reused for - the server. + Routinator Docker images are built with Alpine Linux. The supported + CPU architectures are shown on the `Docker Hub Routinator page + `_ per Routinator + version (aka Docker "tag") in the ``OS/ARCH`` column. - First, create a Docker volume to persist the TAL files in: + To run Routinator as a background daemon with the default settings (RTR + server on port 3323 and HTTP server on port 8323) can be done like so: .. code-block:: bash - sudo docker volume create routinator-tals - - Then run a disposable container to install the TALs: - - .. code-block:: bash - - sudo docker run --rm -v routinator-tals:/home/routinator/.rpki-cache/tals \ - nlnetlabs/routinator init -f --accept-arin-rpa - - Finally, launch the detached container named *routinator*, exposing - the :term:`RPKI-to-Router (RPKI-RTR)` protocol on port 3323 and HTTP - on port 8323: - - .. code-block:: bash - - sudo docker run -d --restart=unless-stopped --name routinator -p 3323:3323 \ - -p 8323:8323 -v routinator-tals:/home/routinator/.rpki-cache/tals \ - nlnetlabs/routinator + sudo docker run -d --restart=unless-stopped --name routinator \ + -p 3323:3323 \ + -p 8323:8323 \ + nlnetlabs/routinator The Routinator container is known to run successfully run under `gVisor `_ for additional isolation. + To adjust the configuration you can pass command line arguments to + Routinator (try :option:`--help` for more information) and/or supply your + own Routinator configuration file (by mapping it from the host into + the container using ``-v host/path/to/routinator.conf:/etc/routinator.conf`` + and passing ``--config /etc/routinator.conf`` when running the container). + + To persist the RPKI cache data you can create a separate Docker volume + and mount it into the container like so: + + .. code-block:: bash + + sudo docker volume create rpki-cache + sudo docker run \ + -v rpki-cache:/home/routinator/.rpki-cache \ + nlnetlabs/routinator + .. versionadded:: 0.9.0 RPM packages .. versionadded:: 0.11.0 Debian packages for ``armhf`` and ``arm64`` architecture .. versionadded:: 0.11.2 Ubuntu packages for Jammy 22.04 (LTS) +.. deprecated:: 0.12.0 + ``routinator-init`` and ``--accept-arin-rpa`` Updating -------- @@ -389,11 +342,14 @@ Updating .. group-tab:: Docker - Upgrading to the latest version of Routinator can be done with: + Assuming that you run Docker with image `nlnetlabs/routinator`, upgrading + to the latest version can be done by running the following commands: .. code-block:: text - docker run -it nlnetlabs/routinator:latest + sudo docker pull nlnetlabs/routinator + sudo docker rm --force routinator + sudo docker run nlnetlabs/routinator Installing Specific Versions ---------------------------- @@ -510,5 +466,5 @@ a specific version, if needed. .. code-block:: text - docker run -it nlnetlabs/routinator:v0.9.0-rc2 + sudo docker run nlnetlabs/routinator:v0.9.0-rc2 diff --git a/doc/manual/source/manual-page.rst b/doc/manual/source/manual-page.rst index 12fdebb..dd9db11 100644 --- a/doc/manual/source/manual-page.rst +++ b/doc/manual/source/manual-page.rst @@ -4,8 +4,6 @@ Manual Page Synopsis -------- -:program:`routinator` [``options``] :subcmd:`init` [``init-options``] - :program:`routinator` [``options``] :subcmd:`vrps` [``vrps-options``] [:samp:`-o {output-file}`] [:samp:`-f {format}`] :program:`routinator` [``options``] :subcmd:`validate` [``validate-options``] [:samp:`-a {asn}`] [:samp:`-p {prefix}`] @@ -49,15 +47,6 @@ The available options are: See `CONFIGURATION FILE`_ below for more information on the format and contents of the configuration file. -.. option:: -b dir, --base-dir=dir - - Specifies the base directory to keep status information in. Unless - overwritten by the :option:`-r` or :option:`-t` options, the local - repository will be kept in the sub-directory :file:`repository` and the - TALs will be kept in the sub-directory :file:`tals`. - - If omitted, the base directory defaults to :file:`$HOME/.rpki-cache`. - .. option:: -r dir, --repository-dir=dir Specifies the directory to keep the local repository in. This is @@ -65,12 +54,43 @@ The available options are: and thus is a copy of all the data referenced via the trust anchors. -.. option:: -t dir, --tal-dir=dir + If omitted, defaults to :file:`$HOME/.rpki-cache/repository`. - Specifies the directory containing the trust anchor locators (TALs) to - use. Trust anchor locators are the starting points for collecting and - validating RPKI data. See `TRUST ANCHOR LOCATORS`_ for more information - on what should be present in this directory. +.. option:: --no-rir-tals + + If present, Routinator will not use the bundled trust anchor locators + (TALs) of the five Regional Internet Registries (RIRs). + + Trust anchor locators are the starting points for collecting and + validating RPKI data. Each of the five RIRs provides a TAL that adds + resources from their area. For normal production installations, these + are the only TALs that should be used. + + Using this option as well as the :option:`--tal` and + :option:`--extra-tals-dir` options you can change which TALs + Routinator should use. + +.. option:: --tal=name + + Use the bundled TAL with the given name in addition to any other TAL. + + Each RIR TAL is available through this option as well as TALs for a + few select test environments. If you use this option with the name + *list*, Routinator will print a list of all available bundled TALS and + exit. + + The option can be given more than once. + +.. option:: --extra-tals-dir=dir + + Specifies a directory containing additional trust anchor locators + (TALs) to use. Routinator will use all files in this directory with + an extension of *.tal* as TALs. These files need to be in the format + described by :rfc:`8630`. + + Note that Routinator will use all TALs provided. That means that if a + TAL in this directory is one of the bundled TALs, then these resources + will be validated twice. .. option:: -x file, --exceptions=file @@ -385,52 +405,6 @@ Commands Routinator provides a number of operations around the local RPKI repository. These can be requested by providing different commands on the command line. -.. subcmd:: init - - Prepares the local repository directories and the TAL directory for - running Routinator. Specifically, makes sure the local repository - directory exists, and creates the TAL directory and fills it with the - desired TALs. - - For more information about TALs, see `TRUST ANCHOR LOCATORS`_ below. - - .. option:: -f, --force - - Forces installation of the TALs even if the TAL directory already - exists. - - .. option:: --rir-tals - - Selects the production TALs of the five RIRs for installation. If - no other TAL selection options are provided, this option is - assumed. - - .. option:: --rir-test-tals - - Selects the bundled TALs for RIR testbeds for installation. - - .. option:: --tal=name - - Selects the bundled TAL with the provided name for installation. - - .. option:: --skip-tal=name - - Deselects the bundled TAL with the given name. - - .. option:: --list-tals - - List all bundled TALs and exit. The list also shows which TALs are - selected by the :option:`--rir-tals` and :option:`--rir-test-tals` - options. - - .. option:: --accept-arin-rpa - - Before you can use the ARIN TAL, you need to agree to the ARIN - Relying Party Agreement (RPA). You can find it at - https://www.arin.net/resources/manage/rpki/rpa.pdf and explicitly - agree to it via this option. This explicit agreement is necessary - in order to install the ARIN TAL. - .. subcmd:: vrps This command requests that Routinator update the local repository and @@ -707,8 +681,8 @@ These can be requested by providing different commands on the command line. This command causes Routinator to act as a server for the RPKI-to-Router (RTR) and HTTP protocols. In this mode, Routinator will - read all the TALs (See `TRUST ANCHOR LOCATORS`_ below) and will stay - attached to the terminal unless the :option:`-d` option is given. + read all the Trust Anchor Locators and will stay attached to the + terminal unless the :option:`-d` option is given. The server will periodically update the local repository, every ten minutes by default, notify any clients of changes, and let them fetch @@ -959,27 +933,6 @@ These can be requested by providing different commands on the command line. the given file instead of displaying it. Use - to output the manual page to standard output. -Trust Anchor Locators ---------------------- - -RPKI uses trust anchor locators, or TALs, to identify the location and public -keys of the trusted root CA certificates. Routinator keeps these TALs in -files in the TAL directory which can be set by the :option:`-t` option. If -the :option:`-b` option is used instead, the TAL directory will be in the -subdirectory *tals* under the directory specified in this option. The default -location, if no options are used at all is :file:`$HOME/.rpki-cache/tals`. - -Routinator comes with a set of commonly used TALs that can be used to -populate the TAL directory via the init command. By default, the command will -install the TALs of the five Regional Internet Registries (RIRs) necessary -for the complete global RPKI repository. - -If the directory does exist, Routinator will use all files with an extension -of *.tal* in this directory. This means that you can add and remove trust -anchors by adding and removing files in this directory. If you add files, -make sure they are in the format described by :rfc:`7730` or the upcoming -:rfc:`8630`. - Configuration File ------------------ @@ -1003,9 +956,18 @@ All values can be overridden via the command line options. A string containing the path to the directory to store the local repository in. This entry is mandatory. - tal-dir - A string containing the path to the directory that contains the - Trust Anchor Locators. This entry is mandatory. + no-rir-tals + A boolean specifying whether the five RIR Trust Anchor Locators + (TALs) should not be added to the set of evaluated TALs. If + missing, the RIR TALs will be used. + + tals + A list of strings, each containing the name of a bundled TAL to + be added to the set of TALs to be evaluated. + + extra-tals-dir + A string containing the path to a directory that contains + additional TALs. exceptions A list of strings, each containing the path to a file with local diff --git a/etc/routinator.conf.example b/etc/routinator.conf.example index 4f58ed6..5c2c9fd 100644 --- a/etc/routinator.conf.example +++ b/etc/routinator.conf.example @@ -21,7 +21,13 @@ # repository-dir = "..." -# Trust Anchor Locator (TAL) directory +# Do not use bundled RIR TALs. +#no-rir-tals = false + +# Use additional bundled TALs. +#tals = [ "apnic-testbed", "nlnetlabs-testbed" ] + +# Directory with additional TALs. # # All the files with the extension ".tal" in this directory are treated as # trust anchor locators for RPKI validation. @@ -29,9 +35,7 @@ repository-dir = "..." # A relative path is interpreted with respect to the directory this config # lives in. # -# This setting is mandatory. -# -tal-dir = "..." +#extra-tals-dir = "..." # Local exceptions files # diff --git a/etc/routinator.conf.system-service b/etc/routinator.conf.system-service index ca096b2..767547c 100644 --- a/etc/routinator.conf.system-service +++ b/etc/routinator.conf.system-service @@ -1,23 +1,20 @@ # Configuration for Running Routinator as a System Service # ======================================================== # -# This configuration assumes that the TALs are placed in -# /var/lib/routinator/tals and the repository cache is maintained in +# This configuration assumes that the repository cache is maintained in # /var/lib/routinator/rpki-cache. # # It will start Routinator with an RTR server listening on port 3323 and # an HTTP server listening on port 8323. Both are limited to localhost by # default. # -# You can use this configuration as -# /etc/routinator/routinator.conf and start Routinator with -# --config /etc/routinator/routinator.conf. +# You can use this configuration as /etc/routinator/routinator.conf and +# start Routinator with --config /etc/routinator/routinator.conf. # # This file contains only the relevant configuration options. For a complete # example, see etc/routinator.conf.example in the source distribution or # consult the manual page. repository-dir = "/var/lib/routinator/rpki-cache" -tal-dir = "/var/lib/routinator/tals" rtr-listen = ["127.0.0.1:3323"] http-listen = ["127.0.0.1:8323"] diff --git a/pkg/common/routinator-init b/pkg/common/routinator-init deleted file mode 100755 index d1ae17a..0000000 --- a/pkg/common/routinator-init +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -CMD="routinator --config /etc/routinator/routinator.conf init $@" - -if [[ $EUID -eq $(id -u routinator) ]]; then - # We are the routinator user, go! - echo "Running command: $CMD" - $CMD -elif [[ $EUID -eq 0 ]]; then - # We are root, become routinator then go! - echo "Running command as user routinator: $CMD" - su -s /bin/sh -c "$CMD" routinator -else - # We are some other user, is sudo installed? - if command -v sudo &> /dev/null; then - echo "Running command as user routinator via sudo: $CMD" - sudo -u routinator $CMD - else - echo >&2 "Error: Unable to become user 'routinator' to run command: $CMD" - exit 1 - fi -fi diff --git a/pkg/common/service.preset b/pkg/common/service.preset deleted file mode 100644 index 08322c8..0000000 --- a/pkg/common/service.preset +++ /dev/null @@ -1 +0,0 @@ -disable routinator.service diff --git a/pkg/rpm/scriptlets.toml b/pkg/rpm/scriptlets.toml index e6caa29..a00f68d 100644 --- a/pkg/rpm/scriptlets.toml +++ b/pkg/rpm/scriptlets.toml @@ -30,7 +30,8 @@ if [ $1 -eq 1 ] ; then # Ensure that the home directory has the correct permissions chmod ${R_HOME_DIR_PERMS} ${R_HOME_DIR} - systemctl preset routinator.service 2>&1 || : + # Set the Routinator service to start now and on boot + systemctl enable --now routinator.service fi ''' diff --git a/pkg/rules/packages-to-test.yml b/pkg/rules/packages-to-test.yml index 26e0ebe..cfcef1f 100644 --- a/pkg/rules/packages-to-test.yml +++ b/pkg/rules/packages-to-test.yml @@ -23,10 +23,3 @@ target: # exclude: # - image: 'debian:bullseye' # mode: 'upgrade-from-published' -exclude: - - image: 'ubuntu:jammy' - mode: 'upgrade-from-published' - - image: 'centos:7' - mode: 'upgrade-from-published' # disabled due to #773, until at least one version with #773 has been released - - image: 'centos:8' - mode: 'upgrade-from-published' # disabled due to #773, until at least one version with #773 has been released diff --git a/pkg/test-scripts/test-routinator.sh b/pkg/test-scripts/test-routinator.sh index 19bd81e..67d88f8 100755 --- a/pkg/test-scripts/test-routinator.sh +++ b/pkg/test-scripts/test-routinator.sh @@ -6,7 +6,8 @@ set -x case $1 in post-install) echo -e "\nROUTINATOR VERSION:" - routinator --version + VER=$(routinator --version) + echo $VER echo -e "\nROUTINATOR CONF:" cat /etc/routinator/routinator.conf @@ -14,37 +15,44 @@ case $1 in echo -e "\nROUTINATOR DATA DIR:" ls -la /var/lib/routinator - echo -e "\nROUTINATOR SERVICE STATUS BEFORE ENABLE:" - systemctl status routinator || true + # For newer Routinator init is no longer required and the Routinator service should be automatically enabled and + # started, for 0.11.3 and earlier init had to be done first and then the service manually enabled and started: + if [[ "$VER" == "Routinator 0.11.3" ]]; then + echo -e "\nROUTINATOR SERVICE STATUS BEFORE ENABLE:" + systemctl status routinator || true - echo -e "\nINIT ROUTINATOR:" - sudo routinator-init --accept-arin-rpa + echo -e "\nINIT ROUTINATOR:" + sudo routinator-init --accept-arin-rpa - echo -e "\nROUTINATOR DATA DIR AFTER INIT:" - ls -la /var/lib/routinator + echo -e "\nROUTINATOR DATA DIR AFTER INIT:" + ls -la /var/lib/routinator - echo -e "\nENABLE ROUTINATOR SERVICE:" - systemctl enable routinator + echo -e "\nENABLE ROUTINATOR SERVICE:" + systemctl enable routinator - echo -e "\nROUTINATOR SERVICE STATUS AFTER ENABLE:" - systemctl status routinator || true + echo -e "\nROUTINATOR SERVICE STATUS AFTER ENABLE:" + systemctl status routinator || true + + echo -e "\nSTART ROUTINATOR SERVICE:" + systemctl start routinator + + echo -e "\nROUTINATOR TALS DIR:" + ls -la /var/lib/routinator/tals/ + fi + + echo -e "\nROUTINATOR SERVICE SHOULD BE ENABLED:" + systemctl is-enabled --quiet routinator + + echo -e "\nROUTINATOR SERVICE SHOULD BE ACTIVE:" + systemctl is-active --quiet routinator - echo -e "\nSTART ROUTINATOR SERVICE:" - systemctl start routinator - sleep 15s echo -e "\nROUTINATOR LOGS AFTER START:" journalctl --unit=routinator - - echo -e "\nROUTINATOR SERVICE STATUS AFTER START:" - systemctl status routinator - echo -e "\nROUTINATOR MAN PAGE:" - man -P cat routinator - - echo -e "\nROUTINATOR TALS DIR:" - ls -la /var/lib/routinator/tals/ + echo -e "\nROUTINATOR MAN PAGE (first 20 lines only):" + man -P cat routinator | head -n 20 || true echo -e "\nROUTINATOR RPKI CACHE DIR (first 20 lines of ls output only):" ls -ltR /var/lib/routinator/rpki-cache/ | head -n 20 || true @@ -65,10 +73,15 @@ case $1 in echo -e "\nROUTINATOR MAN PAGE:" man -P cat routinator - - echo -e "\nROUTINATOR TALS DIR:" - ls -la /var/lib/routinator/tals/ - + + echo -e "\nOLD ROUTINATOR-INIT SCRIPT SHOULD NOT EXIST:" + if [ ! -f /usr/bin/routinator-init ]; then + echo "Confirmed that /usr/bin/routinator-init does not exist." + else + echo >&2 "ERROR: /usr/bin/routinator-init exists but should have been removed if it was present" + exit 1 + fi + echo -e "\nROUTINATOR RPKI CACHE DIR (first 20 lines of ls output only):" ls -ltR /var/lib/routinator/rpki-cache/ | head -n 20 || true ;; diff --git a/src/collector/base.rs b/src/collector/base.rs index 3a894e0..3c97e1e 100644 --- a/src/collector/base.rs +++ b/src/collector/base.rs @@ -2,11 +2,10 @@ //! //! This is a private module. It’s types are re-exported by the parent. -use std::{fs, io}; use std::collections::HashSet; use std::path::Path; use bytes::Bytes; -use log::{error, info}; +use log::info; use rpki::repository::tal::TalUri; use rpki::uri; use crate::config::{Config, FallbackPolicy}; @@ -47,28 +46,11 @@ pub struct Collector { impl Collector { /// Initializes the collector without creating a value. /// - /// Ensures that the base directory exists. This will _not_ create the - /// base directory to ensure that `routinator init` has been run. + /// Ensures that the collector’s directories exists and creates them if + /// necessary. /// /// The function is called implicitly by [`new`][Self::new]. pub fn init(config: &Config) -> Result<(), Failed> { - if let Err(err) = fs::read_dir(&config.cache_dir) { - if err.kind() == io::ErrorKind::NotFound { - error!( - "Missing repository directory {}.\n\ - You may have to initialize it via \ - \'routinator init\'.", - config.cache_dir.display() - ); - } - else { - error!( - "Failed to open repository directory {}: {}", - config.cache_dir.display(), err - ); - } - return Err(Failed) - } rrdp::Collector::init(config)?; rsync::Collector::init(config)?; Ok(()) diff --git a/src/config.rs b/src/config.rs index b8487c6..78c8254 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,7 +6,7 @@ //! //! [`Config`]: struct.Config.html -use std::{env, fmt, fs}; +use std::{env, fmt, fs, process}; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::io::Read; @@ -19,8 +19,9 @@ use clap::{ crate_version, }; use dirs::home_dir; -use log::{LevelFilter, error}; +use log::{LevelFilter, error, warn}; #[cfg(unix)] use syslog::Facility; +use crate::tals; use crate::error::Failed; @@ -125,8 +126,14 @@ pub struct Config { /// Path to the directory that contains the repository cache. pub cache_dir: PathBuf, - /// Path to the directory that contains the trust anchor locators. - pub tal_dir: PathBuf, + /// Should we not use the RIR TALs? + pub no_rir_tals: bool, + + /// Additional bundled TALs to use. + pub bundled_tals: Vec, + + /// Path to a directory that contains additional trust anchor locators. + pub extra_tals_dir: Option, /// Paths to the local exceptions files. pub exceptions: Vec, @@ -381,7 +388,6 @@ impl Config { /// /// The path arguments in `matches` will be interpreted relative to /// `cur_dir`. - #[allow(clippy::cognitive_complexity)] fn apply_arg_matches( &mut self, matches: &ArgMatches, @@ -391,6 +397,14 @@ impl Config { matches ).expect("bug in command line arguments parser"); + // Quick check: If we have the entry "list" in bundled_tals, we + // are supposed to print the TALs and exit. + if let Some(tals) = args.bundled_tals.as_ref() { + if tals.iter().any(|tal| tal == "list") { + tals::print_tals(); + process::exit(0); + } + } // log_target - Goes first so we can move things out of args later. self.apply_log_matches(&args, cur_dir)?; @@ -399,9 +413,6 @@ impl Config { if let Some(dir) = args.repository_dir { self.cache_dir = cur_dir.join(dir) } - else if let Some(dir) = args.base_dir.as_ref() { - self.cache_dir = cur_dir.join(dir).join("repository") - } if self.cache_dir == Path::new("") { error!( "Couldn’t determine default repository directory: \ @@ -411,22 +422,19 @@ impl Config { return Err(Failed) } - // tal_dir - if let Some(dir) = args.tal_dir { - self.tal_dir = cur_dir.join(dir) + // no_rir_tals + if args.no_rir_tals { + self.no_rir_tals = true } - else if let Some(dir) = args.base_dir.as_ref() { - self.tal_dir = cur_dir.join(dir).join("tals") - } - if self.tal_dir == Path::new("") { - error!( - "Couldn’t determine default TAL directory: \ - no home directory.\n\ - Please specify the repository directory with the -t option." - ); - return Err(Failed) + + // bundled_tals + if let Some(tals) = args.bundled_tals { + self.bundled_tals = tals; } + // extra_tals_dir + self.extra_tals_dir = args.extra_tals_dir.map(|dir| cur_dir.join(dir)); + // exceptions if let Some(list) = args.exceptions { self.exceptions = list.into_iter().map(|path| { @@ -834,7 +842,12 @@ impl Config { let log_target = Self::log_target_from_config_file(&mut file)?; let res = Config { cache_dir: file.take_mandatory_path("repository-dir")?, - tal_dir: file.take_mandatory_path("tal-dir")?, + no_rir_tals: file.take_bool("no-rir-tals")?.unwrap_or(false), + bundled_tals: { + file.take_string_array("tals")? + .unwrap_or_default() + }, + extra_tals_dir: file.take_path("extra-tals-dir")?, exceptions: { file.take_path_array("exceptions")?.unwrap_or_default() }, @@ -985,6 +998,13 @@ impl Config { group: file.take_string("group")?, tal_labels: file.take_string_map("tal-labels")?.unwrap_or_default(), }; + + if file.take_path("tal-dir")?.is_some() { + warn!( + "Ignoring obsolete \"tal-dir\" option in config file {}.", + file.path.display() + ); + } file.check_exhausted()?; Ok(res) @@ -1078,12 +1098,14 @@ impl Config { /// Creates a default config with the given paths. /// - /// Uses default values for everything except for the cache and TAL - /// directories which are provided. - fn default_with_paths(cache_dir: PathBuf, tal_dir: PathBuf) -> Self { + /// Uses default values for everything except for the cache directory + /// which needs to be provided. + fn default_with_paths(cache_dir: PathBuf) -> Self { Config { cache_dir, - tal_dir, + no_rir_tals: false, + bundled_tals: Vec::new(), + extra_tals_dir: None, exceptions: Vec::new(), strict: DEFAULT_STRICT, stale: DEFAULT_STALE_POLICY, @@ -1154,16 +1176,20 @@ impl Config { return Err(Failed) } }; - self.tal_dir = match self.tal_dir.strip_prefix(chroot) { - Ok(dir) => dir.into(), - Err(_) => { - error!( - "Fatal: TAL directory {} not under chroot {}.", - self.tal_dir.display(), chroot.display() - ); - return Err(Failed) - } - }; + if let Some(extra_tals_dir) = self.extra_tals_dir.take() { + self.extra_tals_dir = match extra_tals_dir.strip_prefix( + chroot + ) { + Ok(dir) => Some(dir.into()), + Err(_) => { + error!( + "Fatal: TAL directory {} not under chroot {}.", + extra_tals_dir.display(), chroot.display() + ); + return Err(Failed) + } + }; + } for item in &mut self.exceptions { *item = match item.strip_prefix(chroot) { Ok(path) => path.into(), @@ -1211,10 +1237,12 @@ impl Config { "repository-dir".into(), self.cache_dir.display().to_string().into() ); - res.insert( - "tal-dir".into(), - self.tal_dir.display().to_string().into() - ); + if let Some(extra_tals_dir) = self.extra_tals_dir.as_ref() { + res.insert( + "extra-tals-dir".into(), + extra_tals_dir.display().to_string().into() + ); + } res.insert( "exceptions".into(), toml::Value::Array( @@ -1469,15 +1497,13 @@ impl Default for Config { fn default() -> Self { match home_dir() { Some(dir) => { - let base = dir.join(".rpki-cache"); Config::default_with_paths( - base.join("repository"), - base.join("tals") + dir.join(".rpki-cache/repository"), ) } None => { Config::default_with_paths( - PathBuf::from(""), PathBuf::from("") + PathBuf::from(""), ) } } @@ -1669,17 +1695,21 @@ struct GlobalArgs { #[arg(short, long, value_name="PATH")] config: Option, - /// Sets the base directory for cache and TALs - #[arg(short, long, value_name="PATH")] - base_dir: Option, - /// Sets the repository cache directory #[arg(short, long, value_name="PATH")] repository_dir: Option, - /// Sets the TAL directory - #[arg(short, long, value_name="PATH")] - tal_dir: Option, + /// Do not use the bundled RIR TALs + #[arg(long)] + no_rir_tals: bool, + + /// Add an additional bundled TAL ("list" for a list) + #[arg(long = "tal", value_name="NAME")] + bundled_tals: Option>, + + /// A directory to load additional TALs from + #[arg(long, value_name="PATH")] + extra_tals_dir: Option, /// File with local exceptions (see RFC 8416 for format) #[arg(short = 'x', long, value_name="PATH")] @@ -2568,10 +2598,7 @@ mod test { config.cache_dir, home_dir().unwrap().join(".rpki-cache").join("repository") ); - assert_eq!( - config.tal_dir, - home_dir().unwrap().join(".rpki-cache").join("tals") - ); + assert!(config.extra_tals_dir.is_none()); assert!(config.exceptions.is_empty()); assert_eq!(config.strict, DEFAULT_STRICT); assert_eq!(config.validation_threads, ::num_cpus::get()); @@ -2591,7 +2618,7 @@ mod test { fn good_config_file() { let config = ConfigFile::parse( "repository-dir = \"/repodir\"\n\ - tal-dir = \"taldir\"\n\ + extra-tals-dir = \"taldir\"\n\ exceptions = [\"ex1\", \"/ex2\"]\n\ strict = true\n\ validation-threads = 1000\n\ @@ -2609,7 +2636,10 @@ mod test { ).unwrap(); let config = Config::from_config_file(config).unwrap(); assert_eq!(config.cache_dir.to_str().unwrap(), "/repodir"); - assert_eq!(config.tal_dir.to_str().unwrap(), "/test/taldir"); + assert_eq!( + config.extra_tals_dir.unwrap().to_str().unwrap(), + "/test/taldir" + ); assert_eq!( config.exceptions, vec![PathBuf::from("/test/ex1"), PathBuf::from("/ex2")] @@ -2644,12 +2674,14 @@ mod test { fn minimal_config_file() { let config = ConfigFile::parse( "repository-dir = \"/repodir\"\n\ - tal-dir = \"taldir\"", + extra-tals-dir = \"taldir\"", Path::new("/test/routinator.conf") ).unwrap(); let config = Config::from_config_file(config).unwrap(); assert_eq!(config.cache_dir.to_str().unwrap(), "/repodir"); - assert_eq!(config.tal_dir.to_str().unwrap(), "/test/taldir"); + assert_eq!( + config.extra_tals_dir.unwrap().to_str().unwrap(), "/test/taldir" + ); assert!(config.exceptions.is_empty()); assert!(!config.strict); assert_eq!(config.validation_threads, ::num_cpus::get()); @@ -2668,24 +2700,6 @@ mod test { ); } - #[test] - fn bad_config_file() { - let config = ConfigFile::parse( - "", Path::new("/test/routinator.conf") - ).unwrap(); - assert!(Config::from_config_file(config).is_err()); - let config = ConfigFile::parse( - "repository-dir=\"bla\"", - Path::new("/test/routinator.conf") - ).unwrap(); - assert!(Config::from_config_file(config).is_err()); - let config = ConfigFile::parse( - "tal-dir=\"bla\"", - Path::new("/test/routinator.conf") - ).unwrap(); - assert!(Config::from_config_file(config).is_err()); - } - #[test] fn read_your_own_config() { let out_config = get_default_config(); @@ -2701,13 +2715,12 @@ mod test { #[cfg(unix)] fn basic_args() { let config = process_basic_args(&[ - "routinator", "-r", "/repository", "-t", "tals", + "routinator", "-r", "/repository", "-x", "/x1", "--exceptions", "x2", "--strict", "--validation-threads", "2000", "--syslog", "--syslog-facility", "auth" ]); assert_eq!(config.cache_dir, Path::new("/repository")); - assert_eq!(config.tal_dir, Path::new("/test/tals")); assert_eq!( config.exceptions, [Path::new("/x1"), Path::new("/test/x2")] ); diff --git a/src/engine.rs b/src/engine.rs index 5aae8da..370068d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -18,7 +18,7 @@ /// the accompanying trait [`ProcessPubPoint`] dealing with individual /// publication points. -use std::{fmt, fs, io}; +use std::{fmt, fs}; use std::borrow::Cow; use std::collections::HashMap; use std::fs::File; @@ -39,7 +39,7 @@ use rpki::repository::sigobj::SignedObject; use rpki::repository::tal::{Tal, TalInfo, TalUri}; use rpki::repository::x509::{Time, Validity}; use rpki::uri; -use crate::{collector, store}; +use crate::{collector, store, tals}; use crate::config::{Config, FilterPolicy}; use crate::collector::Collector; use crate::error::Failed; @@ -80,8 +80,11 @@ const CRL_CACHE_LIMIT: usize = 50; /// drives the validation run. #[derive(Debug)] pub struct Engine { - /// The directory to load TALs from. - tal_dir: PathBuf, + /// A list of built-in TALs to use. + bundled_tals: Vec, + + /// An optional directory to load TALs from. + extra_tals_dir: Option, /// A mapping of TAL file names to TAL labels. tal_labels: HashMap, @@ -145,7 +148,8 @@ impl Engine { }; let store = Store::new(config)?; let mut res = Engine { - tal_dir: config.tal_dir.clone(), + bundled_tals: tals::collect_tals(config)?, + extra_tals_dir: config.extra_tals_dir.clone(), tal_labels: config.tal_labels.clone(), tals: Vec::new(), collector, @@ -170,84 +174,79 @@ impl Engine { /// It is not considered an error if there are no TAL files in the TAL /// directory. However, a warning will be logged in this case. pub fn reload_tals(&mut self) -> Result<(), Failed> { - let mut res = Vec::new(); - let dir = match fs::read_dir(&self.tal_dir) { - Ok(dir) => dir, - Err(err) => { - if err.kind() == io::ErrorKind::NotFound { - error!( - "Missing TAL directory {}.\n\ - You may have to initialize it via \ - \'routinator init\'.", - self.tal_dir.display() - ); - } - else { - error!("Failed to open TAL directory: {}.", err); - } - return Err(Failed) - } - }; - for entry in dir { - let entry = match entry { - Ok(entry) => entry, + let mut res = self.bundled_tals.clone(); + if let Some(extra_tals_dir) = self.extra_tals_dir.as_ref() { + let dir = match fs::read_dir(extra_tals_dir) { + Ok(dir) => dir, Err(err) => { - error!( - "Failed to iterate over tal directory: {}", - err + error!("Failed to open TAL directory {}: {}.", + extra_tals_dir.display(), err ); return Err(Failed) } }; + for entry in dir { + let entry = match entry { + Ok(entry) => entry, + Err(err) => { + error!( + "Failed to iterate over tal directory: {}", + err + ); + return Err(Failed) + } + }; - if !entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) { - continue + if !entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) { + continue + } + + let path = entry.path(); + if path.extension().map(|ext| ext != "tal").unwrap_or(true) { + continue + } + + let mut file = match File::open(&path) { + Ok(file) => { + file + } + Err(err) => { + error!( + "Failed to open TAL {}: {}. \n\ + Aborting.", + path.display(), err + ); + return Err(Failed) + } + }; + let mut tal = match Tal::read_named( + self.path_to_tal_label(&path), + &mut file + ) { + Ok(tal) => tal, + Err(err) => { + error!( + "Failed to read TAL {}: {}. \n\ + Aborting.", + path.display(), err + ); + return Err(Failed) + } + }; + tal.prefer_https(); + res.push(tal); } - - let path = entry.path(); - if path.extension().map(|ext| ext != "tal").unwrap_or(true) { - continue - } - - let mut file = match File::open(&path) { - Ok(file) => { - file - } - Err(err) => { - error!( - "Failed to open TAL {}: {}. \n\ - Aborting.", - path.display(), err - ); - return Err(Failed) - } - }; - let mut tal = match Tal::read_named( - self.path_to_tal_label(&path), - &mut file - ) { - Ok(tal) => tal, - Err(err) => { - error!( - "Failed to read TAL {}: {}. \n\ - Aborting.", - path.display(), err - ); - return Err(Failed) - } - }; - tal.prefer_https(); - res.push(tal); } if res.is_empty() { warn!( - "No TALs found in TAL directory. Starting anyway." + "No TALs provided. Starting anyway." ); } res.sort_by(|left, right| { left.info().name().cmp(right.info().name()) }); self.tals = res; + Ok(()) } @@ -285,6 +284,10 @@ impl Engine { pub fn start( &self, processor: P ) -> Result, Failed> { + info!("Using the following TALs:"); + for tal in &self.tals { + info!(" * {}", tal.info().name()); + } Ok(Run::new( self, self.collector.as_ref().map(Collector::start), diff --git a/src/operation.rs b/src/operation.rs index d9d6b59..cc46082 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -12,7 +12,6 @@ #![allow(clippy::unnecessary_wraps)] use std::{fs, io, thread}; -use std::collections::HashSet; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; @@ -31,7 +30,7 @@ use rpki::rtr::server::NotifySender; use tempfile::NamedTempFile; use tokio::sync::oneshot; #[cfg(feature = "rta")] use crate::rta; -use crate::{output, tals, validity}; +use crate::{output, validity}; use crate::config::Config; use crate::error::{ExitError, Failed}; use crate::http::http_listener; @@ -64,7 +63,6 @@ use crate::slurm::LocalExceptions; /// [`from_arg_matches`]: #method.from_arg_matches /// [`run`]: #method.run pub enum Operation { - Init(Init), Server(Server), Vrps(Vrps), Validate(Validate), @@ -86,7 +84,6 @@ impl Operation { /// Adds the command configuration to a clap app. pub fn config_args<'a: 'b, 'b>(app: clap::Command) -> clap::Command { - let app = Init::config_args(app); let app = Server::config_args(app); let app = Vrps::config_args(app); let app = Validate::config_args(app); @@ -107,9 +104,6 @@ impl Operation { config: &mut Config ) -> Result { Ok(match matches.subcommand() { - Some(("init", matches)) => { - Operation::Init(Init::from_arg_matches(matches)?) - } Some(("server", matches)) => { Operation::Server( Server::from_arg_matches(matches, cur_dir, config)? @@ -166,7 +160,6 @@ impl Operation { pub fn run(self, config: Config) -> Result<(), ExitError> { let process = Process::new(config); match self { - Operation::Init(cmd) => cmd.run(process), Operation::Server(cmd) => cmd.run(process), Operation::Vrps(cmd) => cmd.run(process), Operation::Validate(cmd) => cmd.run(process), @@ -181,292 +174,6 @@ impl Operation { } -//------------ Init ---------------------------------------------------------- - -/// Initialize the local repository. -pub enum Init { - /// Only list TALs and exit. - ListTals, - - /// Actually do an initialization. - Init { - /// Force installation of TALs. - /// - /// If the TAL directory is present, we will not touch it unless this - /// flag is `true`. - force: bool, - - /// The set of TALs to install. - tals: Vec<&'static tals::BundledTal>, - } -} - -impl Init { - /// Adds the command configuration to a clap app. - pub fn config_args<'a: 'b, 'b>(app: clap::Command) -> clap::Command { - let mut cmd = clap::Command::new("init") - .about("Initializes the local repository") - .arg(Arg::new("force") - .short('f') - .long("force") - .action(ArgAction::SetTrue) - .help("Overwrite an existing TAL directory") - ) - .arg(Arg::new("rir-tals") - .long("rir-tals") - .action(ArgAction::SetTrue) - .help("Install all RIR production TALs") - ) - .arg(Arg::new("rir-test-tals") - .long("rir-test-tals") - .action(ArgAction::SetTrue) - .help("Install all RIR testbed TALs") - ) - .arg(Arg::new("tal") - .long("tal") - .action(ArgAction::Append) - .help( - "Name a TAL to be installed \ - (--list-tals shows available TALs)" - ) - ) - .arg(Arg::new("skip-tal") - .long("skip-tal") - .action(ArgAction::Append) - .help("Name a TAL not to be in installed") - ) - .arg(Arg::new("decline-arin-rpa") - .long("decline-arin-rpa") - .action(ArgAction::SetTrue) - .help("Same as '--skip-tal arin' (deprecated)") - ) - .arg(Arg::new("list-tals") - .long("list-tals") - .action(ArgAction::SetTrue) - .help("List available TALs and exit") - ) - .after_help( - "If none of --rir-tals, --rir-test-tals, or --tal is \ - given, assumes --rir-tals.\n - \n\ - Additional global options are available. \ - Please consult 'routinator --help' for those." - ); - - for tal in tals::BUNDLED_TALS { - if let Some(opt_in) = tal.opt_in.as_ref() { - cmd = cmd.arg( - Arg::new(opt_in.option_name) - .long(opt_in.option_name) - .action(ArgAction::SetTrue) - .help(opt_in.option_help) - ); - } - } - - app.subcommand(cmd) - } - - /// Creates a command from clap matches. - pub fn from_arg_matches( - matches: &ArgMatches, - ) -> Result { - // Easy out for --list-tals - if matches.get_flag("list-tals") { - return Ok(Init::ListTals) - } - - // Collect the names of all requested TALs. - let mut requested: HashSet<_> = matches.get_many::( - "tal" - ).map(|tals| { - tals.cloned().collect() - }).unwrap_or_default(); - if matches.get_flag("rir-test-tals") { - for tal in tals::BUNDLED_TALS { - if tal.category == tals::Category::RirTest { - requested.insert(tal.name.into()); - } - } - } - // --rir-tals or lack of other TAL commands includes all RIR TALs. - if matches.get_flag("rir-tals") || requested.is_empty() { - for tal in tals::BUNDLED_TALS { - if tal.category == tals::Category::Production { - requested.insert(tal.name.into()); - } - } - } - - // Removed --skip-tal TALs. - if let Some(values) = matches.get_many::("skip-tal") { - for tal in values { - // Be strict to avoid accidents. - if !requested.remove(tal) { - eprintln!("Attempt to skip non-included TAL '{}'", tal); - return Err(Failed) - } - } - } - - // Remove ARIN Tal. - if matches.get_flag("decline-arin-rpa") { - eprintln!( - "Warning: '--decline-arin-rpa' has been replaced \ - by '--skip-tal arin' and \n will be removed." - ); - if !requested.remove("arin") { - eprintln!("Attempt to skip non-included TAL 'arin'"); - return Err(Failed) - } - } - - let mut tals = Vec::new(); - - for tal in tals::BUNDLED_TALS { - if !requested.remove(tal.name) { - continue - } - tals.push(tal); - if let Some(opt_in) = tal.opt_in.as_ref() { - if !matches.get_flag(opt_in.option_name) { - eprintln!("{}", opt_in.message); - return Err(Failed) - } - } - } - - if !requested.is_empty() { - for name in requested { - eprintln!("Unknown TAL '{}'", name); - } - return Err(Failed) - } - - Ok(Init::Init { - force: matches.get_flag("force"), - tals, - }) - } - - /// Initializes the local repository. - /// - /// Tries to create `config.cache_dir` if it doesn’t exist. Creates the - /// `config.tal_dir` if it doesn’t exist and installs the bundled TALs. - /// It also does the latter if the directory exists and `force` is - /// `true`. - pub fn run(self, process: Process) -> Result<(), ExitError> { - let (force, tals) = match self { - Init::ListTals => { - Self::list_tals(); - return Ok(()) - } - Init::Init { force, tals } => (force, tals) - }; - - process.create_cache_dir()?; - - // Check if TAL directory exists and error out if needed. - if let Ok(metadata) = fs::metadata(&process.config().tal_dir) { - if metadata.is_dir() { - if !force { - error!( - "TAL directory {} exists.\n\ - Use -f to force installation of TALs.", - process.config().tal_dir.display() - ); - return Err(Failed.into()); - } - } - else { - error!( - "TAL directory {} exists and is not a directory.", - process.config().tal_dir.display() - ); - return Err(Failed.into()) - } - } - - // Try to create the TAL directory and error out if that fails. - if let Err(err) = fs::create_dir_all(&process.config().tal_dir) { - error!( - "Cannot create TAL directory {}: {}", - process.config().tal_dir.display(), err - ); - return Err(Failed.into()) - } - - // Now write all the TALs. Overwrite existing ones. - for tal in &tals { - Self::write_tal(&process.config().tal_dir, tal.name, tal.content)?; - } - - // Not really an error, but that’s our log level right now. - error!( - "Created local repository directory {}", - process.config().cache_dir.display() - ); - error!( - "Installed {} TALs in {}", - tals.len(), - process.config().tal_dir.display() - ); - - Ok(()) - } - - /// Writes the given tal. - fn write_tal( - tal_dir: &Path, - name: &str, - content: &str, - ) -> Result<(), Failed> { - let mut file = match fs::File::create(tal_dir.join( - format!("{}.tal", name) - )) { - Ok(file) => file, - Err(err) => { - error!( - "Can't create TAL file {}: {}.\n Aborting.", - tal_dir.join(name).display(), err - ); - return Err(Failed); - } - }; - if let Err(err) = file.write_all(content.as_ref()) { - error!( - "Can't create TAL file {}: {}.\n Aborting.", - tal_dir.join(name).display(), err - ); - return Err(Failed); - } - Ok(()) - } - - /// Lists all the bundled TALs and exits. - fn list_tals() { - let max_len = tals::BUNDLED_TALS.iter().map(|tal| - tal.name.len() - ).max().unwrap_or(0) + 2; - - println!(" .---- --rir-tals"); - println!(" | .- --rir-test-tals"); - println!(" V V\n"); - - for tal in tals::BUNDLED_TALS { - match tal.category { - tals::Category::Production => print!(" X "), - tals::Category::RirTest => print!(" X "), - _ => print!(" "), - } - println!( - "{:width$} {}", tal.name, tal.description, width = max_len - ); - } - } -} - - //------------ Server -------------------------------------------------------- /// Run as server. diff --git a/src/tals.rs b/src/tals.rs index e81e60c..8d15f45 100644 --- a/src/tals.rs +++ b/src/tals.rs @@ -1,5 +1,83 @@ //! The TALs bundled with Routinator. +use std::collections::HashMap; +use log::error; +use rpki::repository::tal::Tal; +use crate::config::Config; +use crate::error::Failed; + + +//------------ collect_tals -------------------------------------------------- + +/// Produces the set of bundled TALs to use from config. +pub fn collect_tals(config: &Config) -> Result, Failed> { + let mut res = HashMap::new(); + + // Add all explicitely mentioned TALs. + for name in &config.bundled_tals { + let mut added = false; + for tal in BUNDLED_TALS { + if tal.name == name { + if !res.contains_key(tal.name) { + res.insert(tal.name.to_string(), tal.to_tal()); + } + added = true; + break; + } + } + if !added { + error!("Unknown TAL '{}' in --tal option", name); + return Err(Failed) + } + } + + // Add all the RIR TALs unless specifically disabled. + // + // (We are doing this second because it cannot ever fail.) + if !config.no_rir_tals { + for tal in BUNDLED_TALS { + if + tal.category == Category::Production + && !res.contains_key(tal.name) + { + res.insert(tal.name.to_string(), tal.to_tal()); + + } + } + } + + for tal in res.values_mut() { + tal.prefer_https() + } + + Ok(res.into_values().collect()) +} + + +//------------ print_tals ---------------------------------------------------- + +/// Prints all the bundled TALs to stdout. +pub fn print_tals() { + let max_len = BUNDLED_TALS.iter().map(|tal| + tal.name.len() + ).max().unwrap_or(0) + 2; + + println!(" .---- RIR TALs"); + println!(" | .- RIR test TALs"); + println!(" V V\n"); + + for tal in BUNDLED_TALS { + match tal.category { + Category::Production => print!(" X "), + Category::RirTest => print!(" X "), + _ => print!(" "), + } + println!( + "{:width$} {}", tal.name, tal.description, width = max_len + ); + } +} + //------------ BundledTal ---------------------------------------------------- @@ -14,26 +92,16 @@ pub struct BundledTal { /// The category of the TAL. pub category: Category, - /// Does this TAL need explicit opt-in and if so, how is it to be done? - pub opt_in: Option, - /// The actual content of the TAL. pub content: &'static str, } - -//------------ OptIn --------------------------------------------------------- - -/// Information about performing the opt-in procedure for some TALs. -pub struct OptIn { - /// The command line option for explicitely opting in. - pub option_name: &'static str, - - /// The help text for the command line option. - pub option_help: &'static str, - - /// The text to show when opt-in is missing. - pub message: &'static str, +impl BundledTal { + fn to_tal(&self) -> Tal { + Tal::read_named( + self.name.into(), &mut self.content.as_bytes() + ).expect("bundled broken TAL") + } } @@ -67,49 +135,30 @@ pub static BUNDLED_TALS: &[BundledTal] = &[ name: "afrinic", description: "AFRINIC production TAL", category: Category::Production, - opt_in: None, content: include_str!("../tals/afrinic.tal"), }, BundledTal { name: "apnic", description: "APNIC production TAL", category: Category::Production, - opt_in: None, content: include_str!("../tals/apnic.tal"), }, BundledTal { name: "arin", description: "ARIN production TAL", category: Category::Production, - opt_in: Some(OptIn { - option_name: "accept-arin-rpa", - option_help: - "You have read and accept \ - https://www.arin.net/resources/manage/rpki/rpa.pdf", - message: - "Before we can install the ARIN TAL, you must have read\n\ - and agree to the ARIN Relying Party Agreement (RPA).\n\ - It is available at\n\ - \n\ - https://www.arin.net/resources/manage/rpki/rpa.pdf\n\ - \n\ - If you agree to the RPA, please run the command\n\ - again with the --accept-arin-rpa option." - }), content: include_str!("../tals/arin.tal"), }, BundledTal { name: "lacnic", description: "LACNIC production TAL", category: Category::Production, - opt_in: None, content: include_str!("../tals/lacnic.tal"), }, BundledTal { name: "ripe", description: "RIPE production TAL", category: Category::Production, - opt_in: None, content: include_str!("../tals/ripe.tal"), }, @@ -118,21 +167,18 @@ pub static BUNDLED_TALS: &[BundledTal] = &[ name: "apnic-testbed", description: "APNIC RPKI Testbed", category: Category::RirTest, - opt_in: None, content: include_str!("../tals/apnic-testbed.tal"), }, BundledTal { name: "arin-ote", description: "ARIN Operational Test and Evaluation Environment", category: Category::RirTest, - opt_in: None, content: include_str!("../tals/arin-ote.tal"), }, BundledTal { name: "ripe-pilot", description: "RIPE NCC RPKI Test Environment", category: Category::RirTest, - opt_in: None, content: include_str!("../tals/ripe-pilot.tal"), }, @@ -141,7 +187,6 @@ pub static BUNDLED_TALS: &[BundledTal] = &[ name: "nlnetlabs-testbed", description: "NLnet Labs RPKI Testbed", category: Category::Test, - opt_in: None, content: include_str!("../tals/nlnetlabs-testbed.tal"), } ]; diff --git a/tals/README.md b/tals/README.md index 8e5ca90..46c8f25 100644 --- a/tals/README.md +++ b/tals/README.md @@ -1,13 +1,10 @@ Trust Anchor Locators ===================== -This directory contains the trust anchor locators (TALs) of the five -Regional Internet Registries (RIRs) that are bundled with Routinator -and are necessary for RPKI validation. +This directory contains the trust anchor locators (TALs) that are bundled +with Routinator. These are the TALs of the five Regional Internet Registries +(RIRs) as well as those for a number of test setups. -Please be aware that you can only use the ARIN TAL (in file `arin.tal`) -if you agree to the [ARIN Relying Party Agreement (PDF)]. This TAL has been -included in Routinator with permission by ARIN provided we gain agreement -to the RPA before installation or use. +For more information, please refer to `src/tals.rs` which is the canonical +place to describe and categorize them. -[ARIN Relying Party Agreement (PDF)]: https://www.arin.net/resources/manage/rpki/rpa.pdf