routinator 0.10.2

An RPKI relying party software.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# GitHub Actions workflow for building and testing Routinator O/S DEB and RPM
# packages. Uses GitHub Actions caching to avoid rebuilding Rust cargo-deb and
# Routinator 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.
#
# 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.

name: Packaging
on:
  push:
    branches:
      - main
    tags:
      - v*

defaults:
  run:
    # see: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell
    shell: bash --noprofile --norc -eo pipefail -x {0}

jobs:
  # Use the cargo-deb and cargo-generate-rpm Rust crates to build Debian and RPM
  # packages respectively for installing Routinator. 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
          - "ubuntu:xenial"   # ubuntu/16.04
          - "ubuntu:bionic"   # ubuntu/18.04
          - "ubuntu:focal"    # ubuntu/20.04
          - "debian:stretch"  # debian/9
          - "debian:buster"   # debian/10
          - "debian:bullseye" # debian/11
          - 'centos:7'
          - 'centos:8'
    env:
      CARGO_DEB_VER: 1.28.0
      CARGO_GENERATE_RPM_VER: 0.4.0
      # A Routinator version of the form 'x.y.z-dev' denotes a dev build that is
      # newer than the released x.y.z version but is not yet a new release.
      NEXT_VER_LABEL: dev
    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.
    container: ${{ matrix.image }}

    steps:
    - 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:
        echo "OS_NAME=${MATRIX_IMAGE%:*}" >> $GITHUB_ENV
        echo "OS_REL=${MATRIX_IMAGE#*:}" >> $GITHUB_ENV
      env:
        MATRIX_IMAGE: ${{ matrix.image }}

    # Git clone the Routinator code in the branch we were invoked on.
    - 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.
    - name: Install Rust
      run: |
        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:
        DEBIAN_FRONTEND: noninteractive

    - name: Install compilation and other dependencies
      run: |
        case ${OS_NAME} in
          debian|ubuntu)
            apt-get install -y build-essential jq lintian pkg-config
            ;;
          centos)
            yum install epel-release -y
            yum update -y
            yum install -y jq rpmlint
            yum groupinstall -y "Development Tools"
            ;;
        esac
      env:
        DEBIAN_FRONTEND: noninteractive

    # Speed up Routinator Rust builds by caching unchanged built dependencies.
    # See: https://github.com/actions/cache/blob/master/examples.md#rust---cargo
    - name: Cache Dot Cargo
      uses: actions/cache@v2
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ${{ matrix.image }}-cargo-${{ hashFiles('**/Cargo.lock') }}

    # 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 }}

    - 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: |
        case ${OS_NAME} in
          debian|ubuntu)
            cargo install cargo-deb --version ${CARGO_DEB_VER}
            ;;
        esac

    - name: Install Cargo Generate RPM if needed
      if: steps.cache-cargo-generate-rpm.outputs.cache-hit != 'true'
      run: |
        case ${OS_NAME} in
          centos)
            # Temporary workaround for https://github.com/cat-in-136/cargo-generate-rpm/issues/21
            rustup toolchain install 1.52.0
            cargo +1.52.0 install cargo-generate-rpm --version ${CARGO_GENERATE_RPM_VER} --locked
            ;;
        esac

    # Instruct cargo-deb or cargo-generate-rpm to build the Debian package using the config section
    # in Cargo.toml for the specified "variant".
    - name: Create the package
      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<xxx>
        #   Where it is common to set <xxx> to the O/S name.
        # See:
        #   - https://unix.stackexchange.com/a/190899
        #   - https://www.debian.org/doc/debian-policy/ch-controlfields.html#version
        # 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 '~'.
        #
        # 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.
        ROUTINATOR_VER=$(cargo read-manifest | jq -r '.version' | tr '-' '~')
        PKG_ROUTINATOR_VER=$(echo $ROUTINATOR_VER | sed -e "s/~$NEXT_VER_LABEL/-$NEXT_VER_LABEL/")

        case ${OS_NAME} in
          debian|ubuntu)
            case ${OS_REL} in
              xenial|bionic|stretch) VARIANT_NAME="minimal" ;;
              *)                     VARIANT_NAME="" ;;
            esac

            case ${{ github.event_name }} in
              pull_request) MAINTAINER="${{ github.actor }} <unknown@email.address>" ;;
              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 "routinator (${PKG_ROUTINATOR_VER}) unstable; urgency=medium" >target/debian/changelog
            echo "  * See: https://github.com/NLnetLabs/routinator/releases/tag/v${ROUTINATOR_VER}" >>target/debian/changelog
            echo " -- maintainer ${MAINTAINER}  ${RFC5322_TS}" >>target/debian/changelog
            DEB_VER="${PKG_ROUTINATOR_VER}-1${OS_REL}"

            if [[ "${VARIANT_NAME}" == "" ]]; then
              cargo deb --deb-version ${DEB_VER} -v -- --locked
            else
              cargo deb --deb-version ${DEB_VER} --variant ${VARIANT_NAME} -v -- --locked
            fi
            ;;
          centos)
            # Build and strip Routinator as cargo generate-rpm doesn't do this for us
            cargo build --release --locked
            strip -s target/release/routinator

            # Fix the version string to be used for the RPM package
            sed -i -e "s/$ROUTINATOR_VER/$PKG_ROUTINATOR_VER/" Cargo.toml

            # Select the correct systemd service unit file for the target operating system
            case ${MATRIX_IMAGE} in
            centos:7)
              SYSTEMD_SERVICE_UNIT_FILE="routinator-minimal.routinator.service"
              ;;
            *)
              SYSTEMD_SERVICE_UNIT_FILE="routinator.routinator.service"
              ;;
            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/routinator.service
    
            cargo generate-rpm
            ;;
        esac
      env:
        MATRIX_IMAGE: ${{ matrix.image }}

    # See what O/S specific linting tools think of our package.
    - name: Verify the DEB package
      run: |
        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 package. The artifact will be available via the GH
    # Actions job summary and build log pages, but only to users 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 Routinator installation.
    - name: Upload package
      uses: actions/upload-artifact@v2
      with:
        name: ${{ env.OS_NAME }}_${{ env.OS_REL }}
        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.
  pkg-test:
    name: pkg-test
    needs: pkg
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        image: # can't use complex values here, only primitive values are allowed
          - "ubuntu:xenial"   # ubuntu/16.04
          - "ubuntu:bionic"   # ubuntu/18.04
          - "ubuntu:focal"    # ubuntu/20.04
          - "debian:stretch"  # debian/9
          - "debian:buster"   # debian/10
          - "debian:bullseye" # debian/11
          - "centos:7"
          - "centos:8"
        mode:
          - 'fresh-install'
          - 'upgrade-from-published'
        # if we later add a new O/S or variant we won't have yet ever published
        # the package so can't do a test upgrade over last published version. In
        # that case add lines here like so to disable the upgrade from published
        # test for that O/S (remember to change debian:bullseye to the correct
        # O/S name!):
        #
        # exclude:
        #   - image: 'debian:bullseye'
        #     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.
    - 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:
        OS_NAME=${MATRIX_IMAGE%:*}
        OS_REL=${MATRIX_IMAGE#*:}

        echo "OS_NAME=${OS_NAME}" >> $GITHUB_ENV
        echo "OS_REL=${OS_REL}" >> $GITHUB_ENV
        echo "LXC_IMAGE=images:${OS_NAME}/${OS_REL}/cloud" >> $GITHUB_ENV
      env:
        MATRIX_IMAGE: ${{ matrix.image }}

    - name: Download package
      uses: actions/download-artifact@v2
      with:
        name: ${{ env.OS_NAME }}_${{ env.OS_REL }}

    - name: Add current user to LXD group
      run: |
        sudo usermod --append --groups lxd $(whoami)

    - name: Initialize LXD
      run: |
        sudo lxd init --auto

    - name: Check LXD configuration
      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.
        sg lxd -c "lxc launch ${LXC_IMAGE} -c security.nesting=true testcon"

    # 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: |
        echo "Waiting for cloud-init.."
        while ! sudo lxc exec testcon -- ls -la /var/lib/cloud/data/result.json; do
          sleep 1s
        done

        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 LXC container
      run: |
        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 previously published package
      if: ${{ matrix.mode == 'upgrade-from-published' }}
      run: |
        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 routinator"
            ;;
          centos)
            echo '[nlnetlabs]' >$HOME/nlnetlabs.repo
            echo 'name=NLnet Labs' >>$HOME/nlnetlabs.repo
            echo 'baseurl=https://packages.nlnetlabs.nl/linux/centos/$releasever/main/$basearch' >>$HOME/nlnetlabs.repo
            echo 'enabled=1' >>$HOME/nlnetlabs.repo
            sg lxd -c "lxc file push $HOME/nlnetlabs.repo testcon/etc/yum.repos.d/"
            sg lxd -c "lxc exec testcon -- rpm --import https://packages.nlnetlabs.nl/aptkey.asc"
            sg lxd -c "lxc exec testcon -- yum install -y routinator"
            ;;
        esac

    - name: Install new package
      if: ${{ matrix.mode == 'fresh-install' }}
      run: |
        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: |
        echo -e "\nROUTINATOR VERSION:"
        sg lxd -c "lxc exec testcon -- routinator --version"

        echo -e "\nROUTINATOR CONF:"
        sg lxd -c "lxc exec testcon -- cat /etc/routinator/routinator.conf"

        echo -e "\nROUTINATOR DATA DIR:"
        sg lxd -c "lxc exec testcon -- ls -la /var/lib/routinator"

        echo -e "\nROUTINATOR SERVICE STATUS BEFORE ENABLE:"
        sg lxd -c "lxc exec testcon -- systemctl status routinator || true"

        echo -e "\nINIT ROUTINATOR:"
        sg lxd -c "lxc exec testcon -- sudo routinator-init --accept-arin-rpa"

        echo -e "\nROUTINATOR DATA DIR AFTER INIT:"
        sg lxd -c "lxc exec testcon -- ls -la /var/lib/routinator"

        echo -e "\nENABLE ROUTINATOR SERVICE:"
        sg lxd -c "lxc exec testcon -- systemctl enable routinator"

        echo -e "\nROUTINATOR SERVICE STATUS AFTER ENABLE:"
        sg lxd -c "lxc exec testcon -- systemctl status routinator || true"

        echo -e "\nSTART ROUTINATOR SERVICE:"
        sg lxd -c "lxc exec testcon -- systemctl start routinator"
        
        sleep 15s
        echo -e "\nROUTINATOR LOGS AFTER START:"
        sg lxd -c "lxc exec testcon -- journalctl --unit=routinator"

        echo -e "\nROUTINATOR SERVICE STATUS AFTER START:"
        sg lxd -c "lxc exec testcon -- systemctl status routinator"
        
        echo -e "\nROUTINATOR MAN PAGE:"
        sg lxd -c "lxc exec testcon -- man -P cat routinator"

        echo -e "\nROUTINATOR TALS DIR:"
        sg lxd -c "lxc exec testcon -- ls -la /var/lib/routinator/tals/"

        echo -e "\nROUTINATOR RPKI CACHE DIR (first 20 lines of ls output only):"
        sg lxd -c "lxc exec testcon -- ls -ltR /var/lib/routinator/rpki-cache/ | head -n 20"

    - name: Install new package
      if: ${{ matrix.mode == 'upgrade-from-published' }}
      run: |
        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' }}
      run: |
        echo -e "\nROUTINATOR VERSION:"
        sg lxd -c "lxc exec testcon -- routinator --version"

        echo -e "\nROUTINATOR CONF:"
        sg lxd -c "lxc exec testcon -- cat /etc/routinator/routinator.conf"

        echo -e "\nROUTINATOR DATA DIR:"
        sg lxd -c "lxc exec testcon -- ls -la /var/lib/routinator"

        echo -e "\nROUTINATOR SERVICE STATUS:"
        sg lxd -c "lxc exec testcon -- systemctl status routinator || true"
        
        echo -e "\nROUTINATOR MAN PAGE:"
        sg lxd -c "lxc exec testcon -- man -P cat routinator"

        echo -e "\nROUTINATOR TALS DIR:"
        sg lxd -c "lxc exec testcon -- ls -la /var/lib/routinator/tals/"

        echo -e "\nROUTINATOR RPKI CACHE DIR (first 20 lines of ls output only):"
        sg lxd -c "lxc exec testcon -- ls -ltR /var/lib/routinator/rpki-cache/ | head -n 20"