name: CI on: push: branches-ignore: - coverity_scan pull_request: env: PANIC_ACTION: "gdb -batch -x raddb/panic.gdb %e %p 1>&0 2>&0" ALT_OPENSSL: "3.0.8" CI: 1 GH_ACTIONS: 1 DEBIAN_FRONTEND: noninteractive APT_OPTS: "-y --no-install-recommends" LDAP_TEST_SERVER: 127.0.0.1 LDAP_TEST_SERVER_PORT: 3890 REST_TEST_SERVER: 127.0.0.1 REST_TEST_SERVER_PORT: 8080 REST_TEST_SERVER_SSL_PORT: 8443 SQL_MYSQL_TEST_SERVER: mariadb SQL_POSTGRESQL_TEST_SERVER: postgres ASAN_OPTIONS: symbolize=1 detect_leaks=1 detect_stack_use_after_return=1 LSAN_OPTIONS: fast_unwind_on_malloc=0:malloc_context_size=50 UBSAN_OPTIONS: print_stacktrace=1 jobs: pre-ci: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} selfhosted: ${{ github.repository_owner == 'FreeRADIUS' && '1' || '0' }} docker_prefix: ${{ github.repository_owner == 'FreeRADIUS' && 'docker.internal.networkradius.com/' || '' }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@master ci: needs: pre-ci if: ${{ needs.pre-ci.outputs.should_skip != 'true' }} runs-on: ${{ matrix.os.runs_on }} container: image: ${{ matrix.os.docker }} strategy: fail-fast: false matrix: os: # # runs_on - where GitHub will spin up the runner, either # "self-hosted", or the name of a GitHub VM image. # code - the name/version of the OS (for step evaluations below) # docker - the docker image name, if containers are being used # name - used in the job name only # - runs_on: "${{ needs.pre-ci.outputs.selfhosted == '1' && 'self-hosted' || 'ubuntu-20.04' }}" docker: "${{ needs.pre-ci.outputs.selfhosted == '1' && 'docker.internal.networkradius.com/self-hosted' || 'ubuntu:20.04' }}" name: "${{ needs.pre-ci.outputs.selfhosted == '1' && 'self' || 'gh' }}-ubuntu20" code: "ubuntu2004" env: - { CC: gcc, DO_BUILD: yes, LIBS_OPTIONAL: no, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-gcc-lean } - { CC: gcc, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-gcc } - { CC: gcc, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: yes, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-gcc-reproducible } - { CC: gcc, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG -O2 -g3", NAME: linux-gcc-O2-g3 } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: no, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-clang-lean } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-clang } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: yes, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-clang-altlibs } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: yes, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-clang-reproducible } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: yes, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG", NAME: linux-clang-sanitizer } - { CC: clang, DO_BUILD: yes, LIBS_OPTIONAL: yes, LIBS_ALT: no, REPRODUCIBLE: no, SANITIZER: no, BUILD_CFLAGS: "-DWITH_EVAL_DEBUG -O2 -g3", NAME: linux-clang-O2-g3 } env: ${{ matrix.env }} # Test names are used in the branch protection rules in GitHub # If you change the names here, or add additional matrix entries, you # must also amend the branch protection fules. name: "v3.2.x-${{ matrix.os.name }}-${{ matrix.env.NAME }}" # The standard GitHub environment contains PostgreSQL and # MySQL already. However when running on hosted GitHub runners # we need to run separate database containers to provide these. services: mariadb: image: ${{ needs.pre-ci.outputs.docker_prefix }}mariadb env: MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes ports: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 postgres: image: ${{ needs.pre-ci.outputs.docker_prefix }}postgres env: POSTGRES_HOST_AUTH_METHOD: trust ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Self-hosted runner container fixes run: | ln -fs /usr/bin/env /usr/local/bin/sudo rm -rf "$HOME"/* - name: Package manager performance improvements run: | sudo sh -c 'echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/02speedup' if command -v mandb >/dev/null; then echo 'man-db man-db/auto-update boolean false' | sudo debconf-set-selections sudo dpkg-reconfigure man-db fi - name: Freshen APT repo metadata run: | sudo apt-get update - name: Install common build dependencies run: | sudo apt-get install ${APT_OPTS} \ autoconf \ build-essential \ debhelper \ devscripts \ dh-make \ fakeroot \ firebird-dev \ freetds-dev \ gawk \ git \ git-lfs \ gnupg \ libcap-dev \ libcollectdclient-dev \ libcurl4-openssl-dev \ libgdbm-dev \ libhiredis-dev \ libidn11-dev \ libiodbc2 \ libiodbc2-dev \ libjson-c-dev \ libkqueue-dev \ libkrb5-dev \ libldap2-dev \ libmemcached-dev \ libmysqlclient-dev \ libnl-3-dev \ libnl-genl-3-dev \ libpam0g-dev \ libpcap-dev \ libpcre3-dev \ libperl-dev \ libpq-dev \ libreadline-dev \ libruby \ libsnmp-dev \ libsqlite3-dev \ libssl-dev \ libtalloc-dev \ libunbound-dev \ libwbclient-dev \ libykclient-dev \ libyubikey-dev \ lintian \ pbuilder \ python-dev \ python3-dev \ ruby-dev \ snmp \ software-properties-common \ quilt - name: Install LLVM 15 for 20.04 if: ${{ matrix.os.code == 'ubuntu2004' && matrix.env.CC == 'clang' }} run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main" sudo apt-get install ${APT_OPTS} clang-15 llvm-15 gdb libclang-rt-15-dev sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 60 && sudo update-alternatives --set clang /usr/bin/clang-15 sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-15 60 && sudo update-alternatives --set llvm-symbolizer /usr/bin/llvm-symbolizer-15 - name: Install GCC if: ${{ matrix.env.CC == 'gcc' }} run: | sudo apt-get install ${APT_OPTS} gcc gdb # # Build using some alternative libraries # # MIT Kerberos -> HEIMDAL Kerberos # OpenSSL 1.0 -> OpenSSL 3.0 # - name: 'Fetch OpenSSL 3.0 SHA' id: opensslshasum if: ${{ matrix.env.LIBS_ALT == 'yes' }} run: | wget -qO- http://www.openssl.org/source/openssl-$ALT_OPENSSL.tar.gz.sha256 | sed -ne 's/^\s\+/shasum=/p' >> $GITHUB_OUTPUT - name: 'Restore OpenSSL 3.0 from the cache' if: ${{ matrix.env.LIBS_ALT == 'yes' }} uses: actions/cache@v3 id: openssl-cache with: path: /opt/openssl/ key: openssl3-${{ steps.opensslshasum.outputs.shasum }} - name: 'Build OpenSSL 3.0 (if cache stale)' if: ${{ matrix.env.LIBS_ALT == 'yes' && steps.openssl-cache.outputs.cache-hit != 'true' }} run: | cd ~ wget https://www.openssl.org/source/openssl-$ALT_OPENSSL.tar.gz tar xzf openssl-$ALT_OPENSSL.tar.gz cd openssl-$ALT_OPENSSL ./Configure --prefix=/opt/openssl --openssldir=. make -j `nproc` make install - name: Use alternative libraries if: ${{ matrix.env.LIBS_ALT == 'yes' }} run: | echo /opt/openssl/lib64 | sudo tee /etc/ld.so.conf.d/openssl3.conf >/dev/null sudo ldconfig sudo apt-get install ${APT_OPTS} heimdal-dev - name: Show versions run: | printf "$CC: " ; $CC --version printf "\nmake: " ; make --version printf "\nkrb5: " ; krb5-config --all || : [ -d /opt/openssl ] && export PATH=/opt/openssl/bin:$PATH printf "\nopenssl: " ; openssl version - uses: actions/checkout@v3 - name: Build eapol_test run: | if [ -d /opt/openssl ]; then # Used by scripts/ci/eapol_test-build.sh export PATH=/opt/openssl/bin:$PATH export EAPOL_TEST_CFLAGS="-I/opt/openssl/include" export EAPOL_TEST_LDFLAGS="-L/opt/openssl/lib64" fi ./scripts/ci/eapol_test-build.sh - name: Build FreeRADIUS run: | export PATH=$(echo "$PATH" | sed -e 's#:/home/linuxbrew/[^:]\+##g') if [ -d /opt/openssl ]; then export PATH=/opt/openssl/bin:$PATH CONFIG_OPENSSL="--with-openssl-lib-dir=/opt/openssl/lib64 --with-openssl-include-dir=/opt/openssl/include" fi if [ $SANITIZER = "yes" ]; then echo "Enabling sanitizers" enable_sanitizers="--enable-address-sanitizer --enable-undefined-behaviour-sanitizer" if [ "`uname`" != "Darwin" ]; then enable_sanitizers="$enable_sanitizers --enable-leak-sanitizer" fi # TODO: libunbound is broken when built with LSAN/ASAN, let's disable it for now. extra_cflags="--without-rlm_unbound" # Temporary hack just to skip and see the result. # memory leak in rlm_{ldap,rest} and problems in perl+llvm rm -rf src/tests/modules/ldap/ rm -rf src/tests/modules/rest/ else enable_sanitizers="" extra_cflags="" fi CFLAGS="${BUILD_CFLAGS}" ./configure -C \ --enable-developer \ ${enable_sanitizers} \ $CONFIG_OPENSSL \ $extra_cflags \ --enable-werror \ --prefix=$HOME/freeradius \ --with-threads=$LIBS_OPTIONAL \ --with-udpfromto=$LIBS_OPTIONAL \ --with-openssl=$LIBS_OPTIONAL \ --with-pcre=$LIBS_OPTIONAL \ --enable-reproducible-builds=${REPRODUCIBLE} make -j $(($(nproc) + 1)) - name: clang scan run: | make -j $(($(nproc) + 1)) scan && [ "$(find build/plist/ -name *.html)" = '' ] if: ${{ matrix.env.CC == 'clang' }} - name: "Clang Static Analyzer: Store assets on failure" uses: actions/upload-artifact@v3 with: name: clang-scan.tgz path: build/plist/**/*.html if: ${{ matrix.env.CC == 'clang' && failure() }} - name: Add OpenResty repository shell: bash run: | wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add - echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list sudo apt-get update - name: Setup git shell: bash run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Install test requisites run: | # Temporarily replace ucf (for config merge) with cp since it's # terribly slow! sudo mv /usr/bin/ucf /usr/bin/ucf.disabled sudo sh -c 'echo "#!/bin/sh" > /usr/bin/ucf' sudo sh -c 'echo "shift && cp -v \$@" >> /usr/bin/ucf' sudo chmod +x /usr/bin/ucf sudo apt-get install ${APT_OPTS} \ apparmor-utils \ ldap-utils \ openresty \ slapd sudo mv -f /usr/bin/ucf.disabled /usr/bin/ucf - name: Database dependencies run: | sudo apt-get install ${APT_OPTS} \ mariadb-client \ postgresql-client slapd - name: Build certificates for REST tests run: | cp -r raddb/certs raddb/restcerts cd ./raddb/restcerts && make ca && make server - name: Setup test databases run: | for i in \ postgresql-setup.sh \ mysql-setup.sh \ openresty-setup.sh \ ldap-setup.sh \ ldap2-setup.sh; do script="./scripts/ci/$i" echo "Calling $i" $script done - name: Configure test database access run: | mysql -h mariadb -uroot -e "CREATE USER 'radius'@'%' IDENTIFIED BY 'radpass';" mysql -u root -h mariadb -e "GRANT ALL ON radius.* TO 'radius'; FLUSH PRIVILEGES;" - name: Run tests run: | [ -d /opt/openssl ] && export PATH=/opt/openssl/bin:$PATH openssl dhparam -out ./raddb/certs/dh -2 128 || \ openssl dhparam -out ./raddb/certs/dh -2 512 || \ true make ci-test - name: Show debug logs on failure if: ${{ failure() }} run: | cat src/tests/radius.log || : # # If the CI has failed and the branch is ci-debug then start a tmate # session. SSH rendezvous point is emited continuously in the job output. # - name: "Debug: Start tmate" uses: mxschmitt/action-tmate@v3 with: limit-access-to-actor: true if: ${{ github.ref == 'refs/heads/ci-debug' && failure() }} ########################################################################################## # FREERADIUS CORE DEVELOPERS ONLY ########################################################################################## # # Direct push access to the main freeradius-server repo has been disabled in an attempt # to keep CI passing reliably. # # The code below will automatically push to the main repository if a commit passes CI in # your fork on a branch that exists in the main repository. # # The code below will only run if PERSONAL_ACCESS_TOKEN is defined in the repository # secrets for your fork of the freeradius-server repo. # # If the above CI checks pass then we auto-merge into the same branch in the # main FR repo (only on push) if the PERSONAL_ACCESS_TOKEN secret is defined, i.e. when # the actor claims to be a FreeRADIUS developer with push access. # # Personal access tokens can be generated via the GitHub website: # # - Click on the Profile menu (top right) # > Settings # > Developer settings # > Personal access tokens # > Generate New Token # - Next, add the following settings and scopes: # Note: FreeRADIUS CI Push # public_repo (checked) # # This will allow any git operations using this PERSONAL_ACCESS_TOKEN to commit code to any # public repository you have access to. # # As this PERSONAL_ACCESS_TOKEN will only ever be accessible from GitHub actions when they are # running from your fork of the FreeRADIUS repo, this shouldn't be a security issue. # # After generating your PERSONAL_ACCESS_TOKEN you will need to add it as a secret to your # repository. # # - Copy your new token # - Click on the Profile menu (top right) # > Your repositories # - Search for freeradius-server # > Click freeradius-server # - Click settings in the tabs on the left # - Click secrets in the menu items on the left # - Click New repository secret # - Name: PERSONAL_ACCESS_TOKEN # Value: # - Click Add secret # # Needed because secrets are not available for evaluation in if conditions # at the job level, so we evaluate the existence of the PERSONAL_ACCESS_TOKEN secret # within a step and export the result instead. We also extract the short # branch name here because it's convenient to do so. # merge-preflight: needs: - ci if: ( github.event_name == 'push' ) && ( github.repository_owner != 'FreeRADIUS' ) && ( github.ref == 'refs/heads/master' || github.ref == 'refs/heads/v3.2.x' ) name: "Merge preflight" runs-on: ubuntu-latest steps: - name: "Report whether PERSONAL_ACCESS_TOKEN secret exists" id: merge-preflight run: | [ -z "$PERSONAL_ACCESS_TOKEN" ] || echo "::set-output name=PERSONAL_ACCESS_TOKEN_EXISTS::1" env: PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} outputs: PERSONAL_ACCESS_TOKEN_EXISTS: ${{ steps.merge-preflight.outputs.PERSONAL_ACCESS_TOKEN_EXISTS }} merge-upstream: needs: - ci - merge-preflight if: needs.merge-preflight.outputs.PERSONAL_ACCESS_TOKEN_EXISTS == '1' runs-on: ubuntu-latest name: "Merge into upstream" steps: - uses: actions/checkout@v3 with: fetch-depth: 0 lfs: false persist-credentials: false # Note: This also opportunistically updates the developer's branch with commits from # the main repository. # This update may fail if the developer has pushed additional commits since the # workflow started. This is normal, and we ignore the failure. - name: "Merge into upstream dev branch and update local branch" run: | BRANCH=${GITHUB_REF#refs/heads/} git remote add upstream https://$USERNAME:$REPO_KEY@github.com/FreeRADIUS/freeradius-server.git git fetch --no-recurse-submodules upstream +refs/heads/*:refs/remotes/upstream/* +refs/tags/*:refs/tags/upstream/* git checkout --progress --force -B upstream-branch "refs/remotes/upstream/$BRANCH" git merge "$BRANCH" --ff-only git push upstream "upstream-branch:$BRANCH" git push origin "$BRANCH" || true env: USERNAME: ${{ github.actor }} REPO_KEY: ${{ secrets.PERSONAL_ACCESS_TOKEN }}