diff --git a/.github/workflows/build_vanagon.yml b/.github/workflows/build_vanagon.yml new file mode 100644 index 000000000..832d9b02d --- /dev/null +++ b/.github/workflows/build_vanagon.yml @@ -0,0 +1,103 @@ +--- +name: Build openvox-server with vanagon + +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + ref: + description: 'Tag, branch, or SHA to build from' + required: true + branch: + description: >- + The release branch being built. Selects the default platform list. + Use `main` for 9.x builds and `8.x` for 8.x builds. + type: choice + options: + - main + - '8.x' + default: 'main' + platform_list: + description: >- + A comma-separated list of vanagon platforms to build for (e.g. + el-9-x86_64,ubuntu-24.04-amd64). Do not include spaces. If not + provided, will use the default server platform list for the branch. + required: false + type: string + vanagon_branch: + description: 'The branch of the vanagon repository to use' + required: false + type: string + default: 'main' + upload_to_s3: + description: >- + Whether to upload the built packages to S3. Note that the uberjar + archives are always uploaded to S3, because the per-platform builds + fetch them from there. + required: false + type: boolean + default: true + use_dev: + description: 'Use the "dev" branch of the shared-actions repo' + required: false + type: boolean + default: false + +# The uberjar jobs build the openvox-server-uberjar project once per variant, +# see packaging/lib/server_packaging.rb for the platforms. The build jobs then +# build openvox-server for every platform, fetching the archives from S3. +jobs: + uberjar: + if: ${{ github.event.inputs.use_dev == 'false' }} + uses: 'openvoxproject/shared-actions/.github/workflows/build_vanagon.yml@main' + with: + ref: ${{ inputs.ref }} + project_name: 'openvox-server-uberjar' + platform_list: 'el-9-x86_64,redhatfips-9-x86_64' + vanagon_branch: ${{ inputs.vanagon_branch }} + upload_to_s3: true + working_directory: 'packaging' + branch: ${{ inputs.branch }} + secrets: inherit + build: + needs: uberjar + if: ${{ github.event.inputs.use_dev == 'false' }} + uses: 'openvoxproject/shared-actions/.github/workflows/build_vanagon.yml@main' + with: + ref: ${{ inputs.ref }} + project_name: 'openvox-server' + platform_list: ${{ inputs.platform_list }} + platforms_key: 'server-vanagon' + vanagon_branch: ${{ inputs.vanagon_branch }} + upload_to_s3: ${{ inputs.upload_to_s3 }} + working_directory: 'packaging' + branch: ${{ inputs.branch }} + secrets: inherit + uberjar_dev: + if: ${{ github.event.inputs.use_dev == 'true' }} + uses: 'openvoxproject/shared-actions/.github/workflows/build_vanagon.yml@dev' + with: + ref: ${{ inputs.ref }} + project_name: 'openvox-server-uberjar' + platform_list: 'el-9-x86_64,redhatfips-9-x86_64' + vanagon_branch: ${{ inputs.vanagon_branch }} + upload_to_s3: true + working_directory: 'packaging' + branch: ${{ inputs.branch }} + secrets: inherit + build_dev: + needs: uberjar_dev + if: ${{ github.event.inputs.use_dev == 'true' }} + uses: 'openvoxproject/shared-actions/.github/workflows/build_vanagon.yml@dev' + with: + ref: ${{ inputs.ref }} + project_name: 'openvox-server' + platform_list: ${{ inputs.platform_list }} + platforms_key: 'server-vanagon' + vanagon_branch: ${{ inputs.vanagon_branch }} + upload_to_s3: ${{ inputs.upload_to_s3 }} + working_directory: 'packaging' + branch: ${{ inputs.branch }} + secrets: inherit diff --git a/Dockerfile b/Dockerfile index 032db589a..943d6eca3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ RUN dnf install -y --enablerepo=crb \ readline-devel \ rpm-build \ ruby \ + ruby-devel \ sqlite-devel \ wget \ zlib \ diff --git a/Gemfile b/Gemfile index 93974779b..d2a7de71f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' def location_for(place, fake_version = nil) - if place.is_a?(String) && place =~ /^(git[:@][^#]*)#(.*)/ + if place.is_a?(String) && place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact elsif place.is_a?(String) && place =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] @@ -11,8 +11,6 @@ def location_for(place, fake_version = nil) end gem 'public_suffix', '>= 4.0.7', '< 8' -# 1.0.0 is the first OpenVoxProject release -gem 'packaging', '~> 1.0', github: 'OpenVoxProject/packaging' gem 'rake', :group => [:development, :test] group :test do @@ -26,6 +24,10 @@ group :test do gem 'docker-api', '>=1.31.0', '< 3' end +group :packaging do + gem 'vanagon', *location_for(ENV['VANAGON_LOCATION'] || 'https://github.com/openvoxproject/vanagon#main') +end + group :release, optional: true do # usually we pin to ~> 2.1, but some of the EoL beaker 6 dependencies require ancient faraday versions # it's all a huge pain and the beaker setup needs to be reworked diff --git a/packaging/.gitignore b/packaging/.gitignore new file mode 100644 index 000000000..5a526e56a --- /dev/null +++ b/packaging/.gitignore @@ -0,0 +1,4 @@ +output/ +tarballs/ +ext/build_metadata.* +vanagon_hosts.log diff --git a/packaging/README.md b/packaging/README.md new file mode 100644 index 000000000..f81bf7c9c --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,65 @@ +# openvox-server packaging + +This directory holds the [vanagon](https://github.com/openvoxproject/vanagon) +configuration that builds the openvox-server rpm and deb packages. The packages +are noarch and are built once per supported platform so each one carries the +right java dependency, dist tag, and maintainer scripts. + +Two vanagon projects make up the build: + +1. `openvox-server-uberjar` builds everything that needs the JVM toolchain: the + `puppet-server-release.jar` uberjar, the vendored gems, and on the FIPS + variant the BouncyCastle FIPS jars. It runs once per variant, on the + platforms specified in `UBERJAR_PLATFORMS` in `lib/server_packaging.rb`, and + its output is a rooted archive, `openvox-server-uberjar-..tar.gz`, + that CI uploads to the artifacts S3 bucket. +2. `openvox-server` builds the package for one platform. The `uberjar-tarball` + component fetches the matching uberjar archive (from S3 in CI, or from + `packaging/output` for local builds) and the `openvox-server` component + installs all authored content from `packaging/resources/`: CLI wrappers and + configs from `resources/files/`, and the systemd unit, defaults file, and + tmpfiles config rendered from the templates next to it. + +The package build for `SOURCE_TARBALL_PLATFORM` in `lib/server_packaging.rb` +also writes `openvox-server-.tar.gz` to `packaging/output/`. That is +the tarball downstream packagers such as the FreeBSD port build from: the jar +and the authored content in the layout of the ezbake source tarball, under a +`puppetserver-/` top level directory. + +Both run through `rake "vox:build[,]"` at the repo root, +which is what the shared CI workflow calls. + +## Local builds + +Requirements: docker and `bundle install` at the repo root. The JDK and +leiningen are installed inside the build container. + +```sh +bundle exec rake "vox:build[openvox-server,el-9-x86_64]" +bundle exec rake "vox:build[openvox-server,ubuntu-24.04-amd64]" +bundle exec rake "vox:build[openvox-server,redhatfips-9-x86_64]" +``` + +A package build first builds the uberjar archive it needs when that is missing +from `packaging/output/`. To build the archive on its own: + +```sh +bundle exec rake "vox:build[openvox-server-uberjar,el-9-x86_64]" +``` + +Set `SERVER_TARBALL_BASE` to a `file://` or `https://` directory URL to fetch +the archive from somewhere else. Packages land in `packaging/output/`. + +The Clojure dependencies can be rebuilt from source before the uberjar build +with `DEP_REBUILD` (a comma separated subset), `DEP_REBUILD_BRANCH`, +`DEP_REBUILD_ORG` and `FULL_DEP_REBUILD_BRANCH`, as with the ezbake build. + +## Service styles + +Two systemd service styles are maintained (see `packaging/resources/systemd/`). +The `direct` style matches the ezbake 4.x packages shipped for OpenVox 9 and is +the default there. It runs java straight from the unit with `Type=notify`, or +`Type=notify-reload` on platforms with systemd 253 or newer. The `wrapper` +style matches the ezbake 2.x packages shipped for OpenVox 8 and is the default +for 8.x versions. It runs the service through the start/stop/reload CLI +wrappers in `resources/files/wrapper/`. Override with `SERVER_SERVICE_STYLE`. diff --git a/packaging/configs/components/openvox-server.rb b/packaging/configs/components/openvox-server.rb new file mode 100644 index 000000000..32c2938a7 --- /dev/null +++ b/packaging/configs/components/openvox-server.rb @@ -0,0 +1,131 @@ +require_relative '../../lib/server_packaging' + +# Assembles all the package contents from the repo checkout. The compiled +# bits (uberjar, vendored gems, FIPS BC jars) come from the uberjar-tarball +# component. +component 'openvox-server' do |pkg, settings, platform| + pkg.url 'file://resources/files' + pkg.version settings[:package_version] + + wrapper_style = settings[:service_style] == :wrapper + + service_template = wrapper_style ? 'puppetserver-wrapper.service.erb' : 'puppetserver-direct.service.erb' + pkg.add_source "file://resources/systemd/#{service_template}", erb: true + pkg.add_source 'file://resources/systemd/puppetserver.sysconfig.erb', erb: true + pkg.add_source 'file://resources/cli-defaults.sh.erb', erb: true + pkg.add_source 'file://resources/tmpfiles.d/puppetserver.conf' if wrapper_style + + pkg.build do + ["#{platform.sed} -i 's/@@VERSION@@/#{settings[:package_version]}/' bin/puppetserver"] + end + + app_dir = '/opt/puppetlabs/server/apps/puppetserver' + + pkg.install_file 'bin/puppetserver', "#{app_dir}/bin/puppetserver", mode: '0755' + pkg.link "../apps/puppetserver/bin/puppetserver", '/opt/puppetlabs/server/bin/puppetserver' + pkg.link "../server/apps/puppetserver/bin/puppetserver", '/opt/puppetlabs/bin/puppetserver' + + cli_apps = %w[ca foreground gem irb prune ruby].map { |app| ["cli/#{app}", app] } + # The wrapper style ships its own version of reload alongside start and stop + cli_apps += wrapper_style ? %w[start stop reload].map { |app| ["wrapper/#{app}", app] } : [['cli/reload', 'reload']] + cli_apps.each do |source, app| + pkg.install_file source, "#{app_dir}/cli/apps/#{app}", mode: '0755' + end + pkg.install_file '../cli-defaults.sh', "#{app_dir}/cli/cli-defaults.sh", mode: '0755' + pkg.install_file 'wrapper/helper-functions.sh', "#{app_dir}/helper-functions.sh", mode: '0755' if wrapper_style + + pkg.install_file 'system-config/services.d/bootstrap.cfg', "#{app_dir}/config/services.d/bootstrap.cfg" + + %w[auth ca global metrics puppetserver web-routes webserver].each do |conf| + pkg.install_configfile "config/conf.d/#{conf}.conf", "/etc/puppetlabs/puppetserver/conf.d/#{conf}.conf" + end + pkg.install_configfile 'config/services.d/ca.cfg', '/etc/puppetlabs/puppetserver/services.d/ca.cfg' + pkg.install_configfile 'config/logback.xml', '/etc/puppetlabs/puppetserver/logback.xml' + pkg.install_configfile 'config/request-logging.xml', '/etc/puppetlabs/puppetserver/request-logging.xml' + + pkg.install_file 'java.security.fips', '/opt/puppetlabs/server/data/puppetserver/java.security.fips' if platform.is_fips? + + # The rendered service unit, defaults file, and cli-defaults.sh land in the + # workdir root, one level above this component's source directory + service_file = File.basename(service_template, '.erb') + pkg.install_service "../#{service_file}", '../puppetserver.sysconfig', 'puppetserver' + pkg.install_file '../puppetserver.conf', '/usr/lib/tmpfiles.d/puppetserver.conf' if wrapper_style + + # Ordering only, so the jar is unpacked before the source tarball below is assembled + pkg.build_requires 'uberjar-tarball' + + # One platform's build also emits the tarball downstream packagers such as + # the FreeBSD port build from. It carries the jar and the authored content + # in the layout of the ezbake source tarball, under the top level directory + # the port expects, and lands in output/ next to the packages. + if platform.name == ServerPackaging::SOURCE_TARBALL_PLATFORM + tarball_root = ServerPackaging.source_tarball_root(settings[:package_version]) + tarball_name = ServerPackaging.source_tarball_name(settings[:package_version]) + pkg.install do + [ + "rm -rf ../#{tarball_root} && mkdir -p ../#{tarball_root}/ext/cli_defaults ../output", + "cp #{app_dir}/puppet-server-release.jar ../#{tarball_root}/", + "cp -r bin cli config system-config ../#{tarball_root}/ext/", + "cp ../cli-defaults.sh ../#{tarball_root}/ext/cli_defaults/", + "#{platform.tar} --sort=name --owner=0 --group=0 --numeric-owner --mtime=@$$SOURCE_DATE_EPOCH --clamp-mtime " \ + "-C .. -czf ../output/#{tarball_name} #{tarball_root}", + ] + end + end + + # User and group creation, kept identical to what the ezbake packages did. + # The rpm variant prefers uid and gid 52 when they are free. + if platform.is_rpm? + pkg.add_preinstall_action ['install', 'upgrade'], + [<<~HERE + getent group puppet >/dev/null || groupadd --system --force --gid 52 puppet + if getent passwd puppet > /dev/null; then + usermod --gid puppet --home /opt/puppetlabs/server/data/puppetserver \ + --comment "puppetserver daemon" puppet || : + else + useradd_options=('--system' '--gid' 'puppet' '--home' '/opt/puppetlabs/server/data/puppetserver' '--shell' "$(which nologin)" '--comment' 'puppetserver daemon') + if ! getent passwd 52 > /dev/null; then + useradd_options+=('--uid' '52') + fi + useradd "${useradd_options[@]}" puppet || : + fi + HERE + ] + else + pkg.add_preinstall_action ['install', 'upgrade'], + [<<~HERE + getent group puppet > /dev/null || \ + groupadd -r puppet || : + if getent passwd puppet > /dev/null; then + usermod --gid puppet \ + --home /opt/puppetlabs/server/data/puppetserver \ + --comment "puppetserver daemon" puppet || : + else + useradd -r --gid puppet \ + --home /opt/puppetlabs/server/data/puppetserver --shell $(which nologin) \ + --comment "puppetserver daemon" puppet || : + fi + HERE + ] + end + + # Fresh install configuration, carried over from ext/ezbake.conf. The agent + # owns the puppet.conf and ssl directories this touches. + postinstall_commands = [ + 'install --owner=puppet --group=puppet -d /opt/puppetlabs/server/data/puppetserver/jruby-gems', + '/opt/puppetlabs/puppet/bin/puppet config set --section server vardir /opt/puppetlabs/server/data/puppetserver', + '/opt/puppetlabs/puppet/bin/puppet config set --section server logdir /var/log/puppetlabs/puppetserver', + '/opt/puppetlabs/puppet/bin/puppet config set --section server rundir /var/run/puppetlabs/puppetserver', + '/opt/puppetlabs/puppet/bin/puppet config set --section server pidfile /var/run/puppetlabs/puppetserver/puppetserver.pid', + '/opt/puppetlabs/puppet/bin/puppet config set --section server codedir /etc/puppetlabs/code', + 'usermod --home /opt/puppetlabs/server/data/puppetserver puppet', + 'install --directory --owner=puppet --group=puppet --mode=775 /opt/puppetlabs/server/data', + 'install --directory /etc/puppetlabs/puppet/ssl', + 'chown -R puppet:puppet /etc/puppetlabs/puppet/ssl', + 'find /etc/puppetlabs/puppet/ssl -type d -print0 | xargs -0 chmod 770', + ] + if platform.is_fips? + postinstall_commands << 'chown puppet:puppet /opt/puppetlabs/server/data/puppetserver/java.security.fips' + end + pkg.add_postinstall_action ['install'], postinstall_commands +end diff --git a/packaging/configs/components/server-uberjar.rb b/packaging/configs/components/server-uberjar.rb new file mode 100644 index 000000000..65becfe50 --- /dev/null +++ b/packaging/configs/components/server-uberjar.rb @@ -0,0 +1,85 @@ +require_relative '../../lib/server_packaging' + +# Prints the jars on the lein classpath for the given profiles, one per line +def classpath_jars(profiles) + "$$LEIN with-profile #{profiles} classpath | tail -1 | tr ':' '\\n'" +end + +# Copies one jar out of a saved classpath listing. The jar versions are only +# known inside the build container once lein has resolved them, so the lookup +# happens there. An absent jar makes cp fail on the empty path. +def copy_jar(jar, listing, destination) + "cp \"$$(grep -E '/#{jar}-[0-9][^/]*\\.jar$$' #{listing})\" #{destination}/" +end + +# Builds the puppetserver uberjar with leiningen and installs the vendored +# gems with the jar's own gem CLI. On FIPS, the jar is built without BouncyCastle +# embedded in the uberjar and the BC FIPS jars are installed next to it for the +# service to load at runtime. +component 'server-uberjar' do |pkg, settings, platform| + pkg.version settings[:package_version] + pkg.build_requires settings[:build_jdk] + + lein_version = settings[:lein_version] + pkg.add_source "https://raw.githubusercontent.com/technomancy/leiningen/#{lein_version}/bin/lein", + sum: settings[:lein_script_sha256], sum_type: 'sha256' + pkg.add_source "https://github.com/technomancy/leiningen/releases/download/#{lein_version}/leiningen-#{lein_version}-standalone.jar", + sum: settings[:lein_jar_sha256], sum_type: 'sha256' + + # The parts of the repo the uberjar build reads. With no primary source, the + # build runs in the workdir root, where these land next to lein. + %w[project.clj src resources].each { |path| pkg.add_source "file://../#{path}" } + + # Each build and install recipe is one shell, so lein is set up at the top of both + lein_setup = "export LEIN_JAR=$$PWD/leiningen-#{lein_version}-standalone.jar LEIN=$$PWD/lein && chmod 0755 lein" + + deps_to_build, dep_branch, rebuild_org = ServerPackaging.clojure_dep_rebuild + pkg.build_requires 'git' unless deps_to_build.empty? + + pkg.build do + commands = [lein_setup] + deps_to_build.each do |lib| + commands << "git clone --no-checkout https://github.com/#{rebuild_org}/#{lib} deps/#{lib}" + commands << "git -C deps/#{lib} fetch origin #{dep_branch}" + commands << "git -C deps/#{lib} checkout FETCH_HEAD" + commands << "(cd deps/#{lib} && $$LEIN install)" + end + commands << "$$LEIN with-profile #{settings[:uberjar_profiles]} uberjar" + commands + end + + app_dir = '/opt/puppetlabs/server/apps/puppetserver' + jars_dir = '/opt/puppetlabs/server/data/puppetserver/jars' + + pkg.install do + commands = [ + lein_setup, + "install -m 0644 target/puppet-server-release.jar #{app_dir}/puppet-server-release.jar", + # The gem install script runs the jar from the working directory + 'cp target/puppet-server-release.jar puppet-server-release.jar', + ] + + if platform.is_fips? + # JRuby's gem install needs the regular BC jars, which the FIPS jar leaves out. + # This may change with JRuby's newer jruby-openssl that is FIPS-aware. If so, + # remove this workaround and let it use the BC FIPS jars instead. + commands << 'mkdir -p ext/classpath-jars' + commands << "#{classpath_jars('user,pkg,provided')} > gem-classpath.txt" + %w[bcpkix-jdk18on bcprov-jdk18on].each do |jar| + commands << copy_jar(jar, 'gem-classpath.txt', 'ext/classpath-jars') + end + end + + commands << 'DESTDIR= bash resources/ext/build-scripts/install-vendored-gems.sh' + + if platform.is_fips? + commands << "install -d #{jars_dir}" + commands << "#{classpath_jars('user,fips-deps,provided')} > fips-classpath.txt" + %w[bc-fips bcpkix-fips bctls-fips].each do |jar| + commands << copy_jar(jar, 'fips-classpath.txt', jars_dir) + end + end + + commands + end +end diff --git a/packaging/configs/components/uberjar-tarball.rb b/packaging/configs/components/uberjar-tarball.rb new file mode 100644 index 000000000..651f00b16 --- /dev/null +++ b/packaging/configs/components/uberjar-tarball.rb @@ -0,0 +1,19 @@ +require_relative '../../lib/server_packaging' + +# Fetches the archive built by the openvox-server-uberjar project and unpacks +# it into place. It carries the uberjar, the vendored gems, and on FIPS +# platforms the BouncyCastle FIPS jars. Everything else in the package is +# authored content installed by the openvox-server component. +component 'uberjar-tarball' do |pkg, settings, platform| + variant = ServerPackaging.uberjar_variant(platform.name) + tarball_name = ServerPackaging.uberjar_archive_name(settings[:package_version], variant) + + pkg.url File.join(settings[:server_tarball_base], tarball_name) + pkg.sha1sum File.join(settings[:server_tarball_base], "#{tarball_name}.sha1") + pkg.version settings[:package_version] + pkg.install_only true + + pkg.install do + ["gunzip -c #{tarball_name} | #{platform.tar} -C / -xf -"] + end +end diff --git a/packaging/configs/platforms/amazon-2-x86_64.rb b/packaging/configs/platforms/amazon-2-x86_64.rb new file mode 100644 index 000000000..77d70c44a --- /dev/null +++ b/packaging/configs/platforms/amazon-2-x86_64.rb @@ -0,0 +1,3 @@ +platform "amazon-2-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/amazon-2023-x86_64.rb b/packaging/configs/platforms/amazon-2023-x86_64.rb new file mode 100644 index 000000000..3147cb4f3 --- /dev/null +++ b/packaging/configs/platforms/amazon-2023-x86_64.rb @@ -0,0 +1,3 @@ +platform "amazon-2023-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/debian-11-amd64.rb b/packaging/configs/platforms/debian-11-amd64.rb new file mode 100644 index 000000000..93239246c --- /dev/null +++ b/packaging/configs/platforms/debian-11-amd64.rb @@ -0,0 +1,3 @@ +platform "debian-11-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/debian-12-amd64.rb b/packaging/configs/platforms/debian-12-amd64.rb new file mode 100644 index 000000000..dcec455c1 --- /dev/null +++ b/packaging/configs/platforms/debian-12-amd64.rb @@ -0,0 +1,3 @@ +platform "debian-12-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/debian-13-amd64.rb b/packaging/configs/platforms/debian-13-amd64.rb new file mode 100644 index 000000000..a11f2012a --- /dev/null +++ b/packaging/configs/platforms/debian-13-amd64.rb @@ -0,0 +1,3 @@ +platform "debian-13-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/el-10-x86_64.rb b/packaging/configs/platforms/el-10-x86_64.rb new file mode 100644 index 000000000..28b4b1c72 --- /dev/null +++ b/packaging/configs/platforms/el-10-x86_64.rb @@ -0,0 +1,3 @@ +platform "el-10-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/el-8-x86_64.rb b/packaging/configs/platforms/el-8-x86_64.rb new file mode 100644 index 000000000..510f248c5 --- /dev/null +++ b/packaging/configs/platforms/el-8-x86_64.rb @@ -0,0 +1,3 @@ +platform "el-8-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/el-9-x86_64.rb b/packaging/configs/platforms/el-9-x86_64.rb new file mode 100644 index 000000000..5449002f4 --- /dev/null +++ b/packaging/configs/platforms/el-9-x86_64.rb @@ -0,0 +1,3 @@ +platform "el-9-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/fedora-42-x86_64.rb b/packaging/configs/platforms/fedora-42-x86_64.rb new file mode 100644 index 000000000..9407981f4 --- /dev/null +++ b/packaging/configs/platforms/fedora-42-x86_64.rb @@ -0,0 +1,3 @@ +platform "fedora-42-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/fedora-43-x86_64.rb b/packaging/configs/platforms/fedora-43-x86_64.rb new file mode 100644 index 000000000..f0de94ba5 --- /dev/null +++ b/packaging/configs/platforms/fedora-43-x86_64.rb @@ -0,0 +1,3 @@ +platform "fedora-43-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/fedora-44-x86_64.rb b/packaging/configs/platforms/fedora-44-x86_64.rb new file mode 100644 index 000000000..e0c4b14b3 --- /dev/null +++ b/packaging/configs/platforms/fedora-44-x86_64.rb @@ -0,0 +1,3 @@ +platform "fedora-44-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/redhatfips-8-x86_64.rb b/packaging/configs/platforms/redhatfips-8-x86_64.rb new file mode 100644 index 000000000..8692d8452 --- /dev/null +++ b/packaging/configs/platforms/redhatfips-8-x86_64.rb @@ -0,0 +1,3 @@ +platform "redhatfips-8-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/redhatfips-9-x86_64.rb b/packaging/configs/platforms/redhatfips-9-x86_64.rb new file mode 100644 index 000000000..21ec6ded6 --- /dev/null +++ b/packaging/configs/platforms/redhatfips-9-x86_64.rb @@ -0,0 +1,3 @@ +platform "redhatfips-9-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/sles-15-x86_64.rb b/packaging/configs/platforms/sles-15-x86_64.rb new file mode 100644 index 000000000..de382fa8a --- /dev/null +++ b/packaging/configs/platforms/sles-15-x86_64.rb @@ -0,0 +1,3 @@ +platform "sles-15-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/sles-16-x86_64.rb b/packaging/configs/platforms/sles-16-x86_64.rb new file mode 100644 index 000000000..0bfc2d10c --- /dev/null +++ b/packaging/configs/platforms/sles-16-x86_64.rb @@ -0,0 +1,3 @@ +platform "sles-16-x86_64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/ubuntu-22.04-amd64.rb b/packaging/configs/platforms/ubuntu-22.04-amd64.rb new file mode 100644 index 000000000..32ed5a12f --- /dev/null +++ b/packaging/configs/platforms/ubuntu-22.04-amd64.rb @@ -0,0 +1,3 @@ +platform "ubuntu-22.04-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/ubuntu-24.04-amd64.rb b/packaging/configs/platforms/ubuntu-24.04-amd64.rb new file mode 100644 index 000000000..7d253f1ed --- /dev/null +++ b/packaging/configs/platforms/ubuntu-24.04-amd64.rb @@ -0,0 +1,3 @@ +platform "ubuntu-24.04-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/ubuntu-25.04-amd64.rb b/packaging/configs/platforms/ubuntu-25.04-amd64.rb new file mode 100644 index 000000000..49d0c4cf3 --- /dev/null +++ b/packaging/configs/platforms/ubuntu-25.04-amd64.rb @@ -0,0 +1,3 @@ +platform "ubuntu-25.04-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/platforms/ubuntu-26.04-amd64.rb b/packaging/configs/platforms/ubuntu-26.04-amd64.rb new file mode 100644 index 000000000..fe59f9212 --- /dev/null +++ b/packaging/configs/platforms/ubuntu-26.04-amd64.rb @@ -0,0 +1,3 @@ +platform "ubuntu-26.04-amd64" do |plat| + plat.inherit_from_default +end diff --git a/packaging/configs/projects/openvox-server-uberjar.rb b/packaging/configs/projects/openvox-server-uberjar.rb new file mode 100644 index 000000000..a7677ac55 --- /dev/null +++ b/packaging/configs/projects/openvox-server-uberjar.rb @@ -0,0 +1,47 @@ +require_relative '../../lib/server_packaging' + +# Builds the puppetserver uberjar and the vendored gems once, as a rooted +# archive that the openvox-server project unpacks on every platform. The +# regular variant builds on el-9 and the FIPS variant on redhatfips-9, see +# ServerPackaging::UBERJAR_PLATFORMS. +project 'openvox-server-uberjar' do |proj| + platform = proj.get_platform + variant = ServerPackaging.uberjar_variant(platform.name) + build_platform = ServerPackaging::UBERJAR_PLATFORMS.fetch(variant) + unless platform.name == build_platform + fail "The #{variant} variant of openvox-server-uberjar builds on #{build_platform}, not #{platform.name}" + end + + proj.description 'OpenVox Server uberjar and vendored gems' + proj.license 'Apache-2.0' + proj.vendor 'Vox Pupuli ' + proj.homepage 'https://github.com/openvoxproject/openvox-server' + + if ENV['OPENVOX_SERVER_VERSION'] && !ENV['OPENVOX_SERVER_VERSION'].empty? + proj.version ENV['OPENVOX_SERVER_VERSION'] + else + proj.version_from_git + end + version = proj.get_version + major = version.split('.').first.to_i + proj.setting(:package_version, version) + + # The output is the archive of the installed tree, not a package + proj.generate_packages false + proj.generate_archives true + + # OpenVox 8 is built with JDK 17 and OpenVox 9 with JDK 21 + proj.setting(:build_jdk, major >= 9 ? 'java-21-openjdk-headless' : 'java-17-openjdk-headless') + proj.setting(:uberjar_profiles, variant == 'fips' ? 'user,pkg-fips,provided' : 'user,pkg,provided') + + proj.setting(:lein_version, '2.13.0') + proj.setting(:lein_script_sha256, '61cf3a0786748238bc7d78e24b77a875d8c4c0704c3bc11f7180e979b46bcc2c') + proj.setting(:lein_jar_sha256, '5f5231f06c3c7924e3241e3dfa52885577fb44ddf8a9ea373d2c5e2f27217565') + + # Everything the component installs below these directories ends up in the archive + proj.directory '/opt/puppetlabs/server/apps/puppetserver' + proj.directory '/opt/puppetlabs/server/data/puppetserver' + proj.directory '/opt/puppetlabs/puppet/lib/ruby/vendor_gems' + + proj.component 'server-uberjar' +end diff --git a/packaging/configs/projects/openvox-server.rb b/packaging/configs/projects/openvox-server.rb new file mode 100644 index 000000000..6461f4b1a --- /dev/null +++ b/packaging/configs/projects/openvox-server.rb @@ -0,0 +1,149 @@ +project 'openvox-server' do |proj| + platform = proj.get_platform + + fail "openvox-server does not build for platform #{platform.name}" unless platform.is_rpm? || platform.is_deb? + + proj.description 'OpenVox Server (puppetserver), the OpenVox certificate authority and catalog compilation service' + proj.license 'Apache-2.0' + proj.vendor 'Vox Pupuli ' + proj.homepage 'https://github.com/openvoxproject/openvox-server' + + if ENV['OPENVOX_SERVER_VERSION'] && !ENV['OPENVOX_SERVER_VERSION'].empty? + proj.version ENV['OPENVOX_SERVER_VERSION'] + else + proj.version_from_git + end + proj.noarch + + version = proj.get_version + openvox_major = version.split('.').first.to_i + os_version = platform.os_version.to_i # Note this changes Ubuntu 26.04 to just 26, for example + proj.target_repo "openvox#{openvox_major}" + proj.setting(:package_version, version) + + # Where the openvox-server-uberjar archives live. CI builds fetch them from + # the public artifacts bucket where the uberjar jobs uploaded them earlier in + # the same run. The vox:build rake task points this at packaging/output for + # local builds. + tarball_base = ENV['SERVER_TARBALL_BASE'] + tarball_base = "https://s3.osuosl.org/openvox-artifacts/openvox-server/#{version}" if tarball_base.to_s.empty? + proj.setting(:server_tarball_base, tarball_base) + + # Two different service styles are used. The direct style matches the + # ezbake 4.x packages shipped for OpenVox 9, running java straight from the + # unit with Type=notify or notify-reload. The wrapper style matches the + # ezbake 2.x packages shipped for OpenVox 8, where the service runs through + # the start/stop/reload CLI wrappers. + default_style = openvox_major >= 9 ? 'direct' : 'wrapper' + proj.setting(:service_style, (ENV['SERVER_SERVICE_STYLE'] || default_style).to_sym) + + # Type=notify-reload needs systemd 253 or newer. Older platforms use + # Type=notify with an explicit ExecReload. + old_systemd = ((platform.is_fips? || platform.is_el?) && os_version <= 9) || + (platform.is_amazon? && os_version <= 2023) || + (platform.is_sles? && os_version <= 15) || + (platform.is_ubuntu? && platform.os_version == '22.04') + proj.setting(:systemd_notify_reload, !old_systemd) + + # Per platform java runtime dependency. Keep this in sync with what each + # distro actually packages. + java_dep = + if platform.is_fips? + # BC-FJA 2.x is only FIPS certified through Java 21, and 1.x through 17 + openvox_major >= 9 ? 'jre-21-headless' : 'jre-17-headless' + elsif platform.is_el? + case [os_version, openvox_major] + in [8 | 9, 8] then 'jre-17-headless' + in [10, 8] then 'jre-21-headless' + in [8, 9] then 'jre-21-headless' + in [9 | 10, 9] then 'jre-25-headless' + else fail "Unknown el version #{platform.os_version} for OpenVox #{openvox_major}" + end + elsif platform.is_fedora? + openvox_major >= 9 ? 'jre-25-headless' : 'jre-21-headless' + elsif platform.is_amazon? + openvox_major >= 9 ? 'java-25-amazon-corretto-headless' : 'java-17-amazon-corretto-headless' + elsif platform.is_sles? + openvox_major >= 9 ? 'java-25-openjdk-headless' : 'java-17-openjdk-headless' + elsif platform.is_deb? + if openvox_major >= 9 + 'openjdk-25-jre-headless' + elsif platform.is_debian? && os_version <= 12 + 'openjdk-17-jre-headless' + else + 'openjdk-21-jre-headless' + end + else + fail "No java dependency known for platform #{platform.name}" + end + # OpenVox 9 rpm packages point at the exact runtime of the required java + # package instead of the alternatives symlink. Debian based packages keep + # /usr/bin/java because the Debian JVM directory paths include the + # architecture and these packages are noarch. At some point we might migrate + # to arch-specific packages to go back to the direct path, but we'll need + # to make that change carefully. OpenVox 8 packages keep /usr/bin/java everywhere + # like ezbake 2.x did. A platform file can override java_bin through settings. + java_bins = { + 'jre-21-headless' => '/usr/lib/jvm/jre-21/bin/java', + 'jre-25-headless' => '/usr/lib/jvm/jre-25/bin/java', + 'java-25-amazon-corretto-headless' => '/usr/lib/jvm/jre-25/bin/java', + 'java-25-openjdk-headless' => '/usr/lib64/jvm/jre-25/bin/java', + } + java_bin = openvox_major >= 9 ? java_bins.fetch(java_dep, '/usr/bin/java') : '/usr/bin/java' + proj.setting(:java_bin, proj.settings[:java_bin] || java_bin) + + java_args = '-Xms2g -Xmx2g' + # OpenVox 9 sets the JRuby logger in code, OpenVox 8 still needs the flag + java_args = "#{java_args} -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger" if openvox_major < 9 + if platform.is_fips? + java_args = "-Djava.security.properties==/opt/puppetlabs/server/data/puppetserver/java.security.fips #{java_args}" + end + proj.setting(:java_args, java_args) + + # JRuby needs these module flags on newer JVMs. The direct style unit passes + # them through JAVA_ARGS_DIST so package upgrades can change them without + # touching the user-editable defaults file, and cli-defaults.sh adds them for + # CLI runs once the installed java is at least the given major version. + if openvox_major >= 9 + proj.setting(:java_args_dist, '--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --enable-native-access=ALL-UNNAMED') + proj.setting(:java_args_dist_min_major, 21) + else + proj.setting(:java_args_dist, '--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED') + proj.setting(:java_args_dist_min_major, 17) + end + + proj.requires java_dep + proj.requires 'tzdata-java' if platform.is_amazon? + proj.requires 'bash' + proj.requires 'procps' + proj.requires 'net-tools' if proj.settings[:service_style] == :wrapper + proj.requires '/usr/bin/which' if platform.is_rpm? + # The ezbake rpms carry a plain systemd requirement on the el-like platforms. + # SLES gets %{?systemd_requires} from the vanagon spec template instead. + proj.requires 'systemd' if platform.is_rpm? && !platform.is_sles? + proj.requires 'adduser' if platform.is_deb? + if openvox_major >= 9 + proj.requires 'openvox-agent', '>= 9.0.0~rc1' + else + proj.requires 'openvox-agent', '>= 8.29.0' + proj.requires 'openvox-agent', '< 9.0.0~' + end + + proj.replaces 'puppetserver' + proj.conflicts 'puppetserver' + + proj.directory '/opt/puppetlabs/bin' + proj.directory '/opt/puppetlabs/server/bin' + proj.directory '/opt/puppetlabs/server/apps/puppetserver' + proj.directory '/opt/puppetlabs/server/data/puppetserver', mode: '0770', owner: 'puppet', group: 'puppet' + proj.directory '/opt/puppetlabs/server/data/puppetserver/jars', mode: '0700', owner: 'puppet', group: 'puppet' + proj.directory '/opt/puppetlabs/server/data/puppetserver/yaml', mode: '0700', owner: 'puppet', group: 'puppet' + proj.directory '/opt/puppetlabs/puppet/lib/ruby/vendor_gems' + proj.directory '/etc/puppetlabs/puppetserver', mode: '0750', owner: 'puppet', group: 'puppet' + proj.directory '/var/log/puppetlabs/puppetserver', mode: '0700', owner: 'puppet', group: 'puppet' + # The direct style lets systemd manage the runtime directory + proj.directory '/var/run/puppetlabs/puppetserver', mode: '0755', owner: 'puppet', group: 'puppet' if proj.settings[:service_style] == :wrapper + + proj.component 'uberjar-tarball' + proj.component 'openvox-server' +end diff --git a/packaging/lib/server_packaging.rb b/packaging/lib/server_packaging.rb new file mode 100644 index 000000000..0401d7c36 --- /dev/null +++ b/packaging/lib/server_packaging.rb @@ -0,0 +1,76 @@ +# Naming and build knobs shared by the rake tasks and the vanagon configs, so +# the archive built by the openvox-server-uberjar project is found under the +# same name by the openvox-server project and by the local build helper. +module ServerPackaging + # Platforms the uberjar project builds on. Both are AlmaLinux 9 images, which + # carry the JDK 17 and JDK 21 packages, so one platform per variant serves + # both release lines. + UBERJAR_PLATFORMS = { + 'regular' => 'el-9-x86_64', + 'fips' => 'redhatfips-9-x86_64', + }.freeze + + # The package build that also emits the source tarball downstream packagers + # such as the FreeBSD port build from. Any one platform would do, this one is + # in the platform lists of both release lines. + SOURCE_TARBALL_PLATFORM = 'el-9-x86_64' + + # Clojure libraries that can be rebuilt from source before the uberjar + # build, in dependency order. There is a circular dependency between + # clj-http-client and trapperkeeper-webserver, but only for tests. + CLOJURE_DEP_BUILD_ORDER = %w[ + clj-kitchensink + clj-i18n + comidi + jvm-ssl-utils + clj-typesafe-config + jruby-deps + trapperkeeper + trapperkeeper-filesystem-watcher + clj-http-client + trapperkeeper-webserver + ring-middleware + jruby-utils + clj-shell-utils + trapperkeeper-authorization + trapperkeeper-metrics + trapperkeeper-scheduler + trapperkeeper-status + trapperkeeper-comidi-metrics + ].freeze + + def self.uberjar_variant(platform_name) + platform_name.start_with?('redhatfips') ? 'fips' : 'regular' + end + + # Matches the archive name vanagon gives the uberjar project's output + def self.uberjar_archive_name(version, variant) + "openvox-server-uberjar-#{version}.#{UBERJAR_PLATFORMS.fetch(variant)}.tar.gz" + end + + # The source tarball keeps the name ezbake gave it, and its top level + # directory keeps the puppetserver name the FreeBSD port expects + def self.source_tarball_name(version) + "openvox-server-#{version}.tar.gz" + end + + def self.source_tarball_root(version) + "puppetserver-#{version}" + end + + # Reads a dependency rebuild request from the environment. Returns the + # libraries to build in order, the git ref to build them from, and the + # GitHub organisation to clone them from. + def self.clojure_dep_rebuild(env = ENV) + org = env.fetch('DEP_REBUILD_ORG', 'openvoxproject') + full_rebuild_branch = env['FULL_DEP_REBUILD_BRANCH'].to_s.strip + return [CLOJURE_DEP_BUILD_ORDER, full_rebuild_branch, org] unless full_rebuild_branch.empty? + + subset = env['DEP_REBUILD'].to_s.split(',').map(&:strip).reject(&:empty?) + return [[], nil, org] if subset.empty? + + unknown = subset - CLOJURE_DEP_BUILD_ORDER + warn "WARNING: Unknown deps in DEP_REBUILD (will be ignored): #{unknown.join(', ')}" unless unknown.empty? + [CLOJURE_DEP_BUILD_ORDER & subset, env.fetch('DEP_REBUILD_BRANCH', 'main'), org] + end +end diff --git a/packaging/resources/cli-defaults.sh.erb b/packaging/resources/cli-defaults.sh.erb new file mode 100644 index 000000000..71243e6e0 --- /dev/null +++ b/packaging/resources/cli-defaults.sh.erb @@ -0,0 +1,21 @@ +INSTALL_DIR="/opt/puppetlabs/server/apps/puppetserver" + +if [ -n "$JRUBY_JAR" ]; then + echo "Warning: the JRUBY_JAR setting is no longer needed and will be ignored." 1>&2 +fi + +java_version=$($JAVA_BIN -version 2>&1 | head -1 | awk -F\" '{ print $2 }') +java_major_version=$(echo $java_version | awk -F. '{ print $1 }') + +if [[ $java_major_version -ge <%= @settings[:java_args_dist_min_major] %> ]]; then + + if ! grep -Eq 'add-opens|enable-native-access' <<<"$JAVA_ARGS"; then + export JAVA_ARGS="<%= @settings[:java_args_dist] %> ${JAVA_ARGS}" + fi + + if ! grep -Eq 'add-opens|enable-native-access' <<<"$JAVA_ARGS_CLI"; then + export JAVA_ARGS_CLI="<%= @settings[:java_args_dist] %> ${JAVA_ARGS_CLI}" + fi +fi + +CLASSPATH="/opt/puppetlabs/server/data/puppetserver/jars/*:${CLASSPATH}" diff --git a/packaging/resources/files/bin/puppetserver b/packaging/resources/files/bin/puppetserver new file mode 100755 index 000000000..202371ede --- /dev/null +++ b/packaging/resources/files/bin/puppetserver @@ -0,0 +1,97 @@ +#!/bin/bash + +#set default privileges to -rw-r----- +umask 027 + +set -a +if [ -r "/etc/default/puppetserver" ] ; then + . /etc/default/puppetserver +elif [ -r "/etc/sysconfig/puppetserver" ] ; then + . /etc/sysconfig/puppetserver +elif [ `uname` == "OpenBSD" ] ; then + JAVA_BIN=$(javaPathHelper -c puppetserver) + JAVA_ARGS="-Xms2g -Xmx2g" + TK_ARGS="" + USER="_puppet" + INSTALL_DIR="/opt/puppetlabs/server/apps/puppetserver" + CONFIG="/etc/puppetlabs/puppetserver/conf.d" +else + echo "You seem to be missing some important configuration files; could not find /etc/default/puppetserver or /etc/sysconfig/puppetserver" >&2 + exit 1 +fi +set +a + +CLI_DIR="${INSTALL_DIR}/cli" +CLI_APP_DIR="${CLI_DIR}/apps" +APPS=`ls ${CLI_APP_DIR} 2>/dev/null` + +############# +# FUNCTIONS # +############# + +# Display usage then exit +function usage { + + if [ "$APPS" == "" ]; then + echo "ERROR: No sub-commands found in ${CLI_APP_DIR}" + exit 1 + fi + + cat < [] + +The most commonly used puppetserver commands are: +EOD + + # Iterate and display commands in the CLI_APP_DIR + for f in $APPS; do + echo " $f" + done + + cat << EOD + +See '$(basename $0) -h' for more information on a specific command. +EOD + exit 0 +} + +function show_version { + cat <&2 + exit 1 + fi +fi + +COMMAND="${JAVA_BIN} ${JAVA_ARGS} ${LOG_APPENDER} \ + -cp '${CLASSPATH}' \ + clojure.main -m puppetlabs.trapperkeeper.main \ + --config ${CONFIG} --bootstrap-config ${BOOTSTRAP_CONFIG} \ + ${TK_ARGS} \ + ${@}" + +pushd "${INSTALL_DIR}" &> /dev/null +if [ "$EUID" = "0" ] && command -v runuser &> /dev/null; then + runuser "${USER}" -s /bin/bash -c "$COMMAND" +elif [ "$EUID" = "$(id -u ${USER})" ]; then + /bin/bash -c "$COMMAND" +elif command -v sudo &> /dev/null; then + sudo -H -u "${USER}" $COMMAND +else + su "${USER}" -s /bin/bash -c "$COMMAND" +fi +popd &> /dev/null diff --git a/packaging/resources/files/cli/gem b/packaging/resources/files/cli/gem new file mode 100755 index 000000000..3884128cb --- /dev/null +++ b/packaging/resources/files/cli/gem @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +umask 0022 + +cli_defaults=${INSTALL_DIR}/cli/cli-defaults.sh + +CLASSPATH=${INSTALL_DIR}/puppet-server-release.jar + +if [ -e "$cli_defaults" ]; then + . $cli_defaults + if [ $? -ne 0 ]; then + echo "Unable to initialize cli defaults, failing irb subcommand." 1>&2 + exit 1 + fi +fi + +"${JAVA_BIN}" $JAVA_ARGS_CLI \ + -cp "$CLASSPATH" \ + clojure.main -m puppetlabs.puppetserver.cli.gem \ + --config "${CONFIG}" -- "$@" diff --git a/packaging/resources/files/cli/irb b/packaging/resources/files/cli/irb new file mode 100755 index 000000000..da23d7102 --- /dev/null +++ b/packaging/resources/files/cli/irb @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +cli_defaults=${INSTALL_DIR}/cli/cli-defaults.sh + +CLASSPATH=${INSTALL_DIR}/puppet-server-release.jar + +if [ -e "$cli_defaults" ]; then + . $cli_defaults + if [ $? -ne 0 ]; then + echo "Unable to initialize cli defaults, failing irb subcommand." 1>&2 + exit 1 + fi +fi + +"${JAVA_BIN}" $JAVA_ARGS_CLI \ + -cp "$CLASSPATH" \ + clojure.main -m puppetlabs.puppetserver.cli.irb \ + --config "${CONFIG}" -- "$@" diff --git a/packaging/resources/files/cli/prune b/packaging/resources/files/cli/prune new file mode 100755 index 000000000..feb807345 --- /dev/null +++ b/packaging/resources/files/cli/prune @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +usage() { + echo "Prune contents of report and bucket directories." + echo + echo "Usage: puppetserver prune []" + echo " bucketdir|reportdir work on either bucketdir or reportdir" + echo " delete data older than this amount of time (default: 14d)" +} + +prune() { + DIR="$1" + AGE=${2:-14d} + puppet apply --no-report --log_level=warning -e "tidy { \$settings::${DIR}: age=>'${AGE}', recurse=>true, rmdirs=>true }" +} + +case $1 in + -h|--help) + usage + exit 0 + ;; + bucketdir|reportdir) + prune "$1" "$2" + ;; + *) + echo "Error: unknown argument." + usage + exit 1 + ;; +esac diff --git a/packaging/resources/files/cli/reload b/packaging/resources/files/cli/reload new file mode 100755 index 000000000..1420fb64a --- /dev/null +++ b/packaging/resources/files/cli/reload @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set +e + +restartfile="/run/puppetlabs/puppetserver/restartcounter" +reload_timeout="${RELOAD_TIMEOUT:-120}" +timeout="$reload_timeout" + +if [ ! -r $restartfile ]; then + echo "Restart counter does not exist or is not readable: ${restartfile}" 1>&2 + exit 1 +fi + +pid="$(systemctl show --value -p MainPID puppetserver)" +if [[ ! "$pid" =~ ^[0-9]+$ ]] || [ "$pid" -eq 0 ]; then + echo "Service not running so cannot be reloaded" 1>&2 + exit 1 +fi + +initial="$(head -n 1 "$restartfile")" +kill -HUP "$pid" >/dev/null 2>&1 +sleep 0.1 +cur="$(head -n 1 "$restartfile")" +while [ "$cur" == "$initial" ] ;do + kill -0 "$pid" >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Process $pid exited before reload had completed" 1>&2 + exit 1 + fi + sleep 1 + cur="$(head -n 1 "$restartfile")" + + ((timeout--)) + if [ $timeout -le 0 ]; then + echo "Reload timed out after $reload_timeout seconds" + exit 1 + fi +done + +exit 0 diff --git a/packaging/resources/files/cli/ruby b/packaging/resources/files/cli/ruby new file mode 100755 index 000000000..bef6a5d1f --- /dev/null +++ b/packaging/resources/files/cli/ruby @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +cli_defaults=${INSTALL_DIR}/cli/cli-defaults.sh + +CLASSPATH=${INSTALL_DIR}/puppet-server-release.jar + +if [ -e "$cli_defaults" ]; then + . $cli_defaults + if [ $? -ne 0 ]; then + echo "Unable to initialize cli defaults, failing irb subcommand." 1>&2 + exit 1 + fi +fi + +"${JAVA_BIN}" $JAVA_ARGS_CLI \ + -cp "$CLASSPATH" \ + clojure.main -m puppetlabs.puppetserver.cli.ruby \ + --config "${CONFIG}" -- "$@" diff --git a/packaging/resources/files/config/conf.d/auth.conf b/packaging/resources/files/config/conf.d/auth.conf new file mode 100644 index 000000000..2f9c7a1d8 --- /dev/null +++ b/packaging/resources/files/config/conf.d/auth.conf @@ -0,0 +1,343 @@ +authorization: { + version: 1 + rules: [ + { + # Allow nodes to retrieve their own catalog + match-request: { + path: "^/puppet/v3/catalog/([^/]+)$" + type: regex + method: [get, post] + } + allow: "$1" + sort-order: 500 + name: "puppetlabs v3 catalog from agents" + }, + { + # Allow services to retrieve catalogs on behalf of others + match-request: { + path: "^/puppet/v4/catalog/?$" + type: regex + method: post + } + deny: "*" + sort-order: 500 + name: "puppetlabs v4 catalog for services" + }, + { + # Allow nodes to retrieve the certificate they requested earlier + match-request: { + path: "/puppet-ca/v1/certificate/" + type: path + method: get + } + allow-unauthenticated: true + sort-order: 500 + name: "puppetlabs certificate" + }, + { + # Allow all nodes to access the certificate revocation list + match-request: { + path: "/puppet-ca/v1/certificate_revocation_list/ca" + type: path + method: get + } + allow-unauthenticated: true + sort-order: 500 + name: "puppetlabs crl" + }, + { + # Allow nodes to request a new certificate + match-request: { + path: "/puppet-ca/v1/certificate_request" + type: path + method: [get, put] + } + allow-unauthenticated: true + sort-order: 500 + name: "puppetlabs csr" + }, + { + # Allow nodes to renew their certificate + match-request: { + path: "/puppet-ca/v1/certificate_renewal" + type: path + method: post + } + # this endpoint should never be unauthenticated, as it requires the cert to be provided. + allow: "*" + sort-order: 500 + name: "puppetlabs certificate renewal" + }, + { + # Allow the CA CLI to access the certificate_status endpoint + match-request: { + path: "/puppet-ca/v1/certificate_status" + type: path + method: [get, put, delete] + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert status" + }, + { + match-request: { + path: "^/puppet-ca/v1/certificate_revocation_list$" + type: regex + method: put + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs CRL update" + }, + { + # Allow the CA CLI to access the certificate_statuses endpoint + match-request: { + path: "/puppet-ca/v1/certificate_statuses" + type: path + method: get + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert statuses" + }, + { + # Allow authenticated access to the CA expirations endpoint + match-request: { + path: "/puppet-ca/v1/expirations" + type: path + method: get + } + allow: "*" + sort-order: 500 + name: "puppetlabs CA cert and CRL expirations" + }, + { + # Allow the CA CLI to access the certificate clean endpoint + match-request: { + path: "/puppet-ca/v1/clean" + type: path + method: put + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert clean" + }, + { + # Allow the CA CLI to access the certificate sign endpoint + match-request: { + path: "/puppet-ca/v1/sign" + type: path + method: post + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert sign" + }, + { + # Allow the CA CLI to access the certificate sign all endpoint + match-request: { + path: "/puppet-ca/v1/sign/all" + type: path + method: post + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert sign all" + }, + { + # Allow unauthenticated access to the status service endpoint + match-request: { + path: "/status/v1/services" + type: path + method: get + } + allow-unauthenticated: true + sort-order: 500 + name: "puppetlabs status service - full" + }, + { + match-request: { + path: "/status/v1/simple" + type: path + method: get + } + allow-unauthenticated: true + sort-order: 500 + name: "puppetlabs status service - simple" + }, + { + match-request: { + path: "/puppet/v3/environments" + type: path + method: get + } + allow: "*" + sort-order: 500 + name: "puppetlabs environments" + }, + { + # Allow nodes to store content in the filebucket. Agents need 'head' + # to test whether content is already stored and 'put' to store it; + # they never read content back out. Note that access for the 'delete' + # method is forbidden by Puppet regardless of the configuration of + # this rule. + match-request: { + path: "/puppet/v3/file_bucket_file" + type: path + method: [head, put] + } + allow: "*" + sort-order: 500 + name: "puppetlabs file bucket file" + }, + { + # Reading content back out of the filebucket is an administrative + # operation, so it is restricted to the CLI rather than granted to + # every agent. A central filebucket is shared by every node that + # backs up to it, and content is addressed only by checksum with no + # record of which node submitted it, so any certificate allowed to + # read here can retrieve content backed up by any node. Sites that + # restore remotely with a different administrative certificate should + # add a rule of their own rather than widening this one. + match-request: { + path: "/puppet/v3/file_bucket_file" + type: path + method: [get, post] + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs file bucket file read" + }, + { + # Allow nodes to access all file_content. Note that access for the + # 'delete' method is forbidden by Puppet regardless of the + # configuration of this rule. + match-request: { + path: "/puppet/v3/file_content" + type: path + method: [get, post] + } + allow: "*" + sort-order: 500 + name: "puppetlabs file content" + }, + { + # Allow nodes to access all file_metadata. Note that access for the + # 'delete' method is forbidden by Puppet regardless of the + # configuration of this rule. + match-request: { + path: "/puppet/v3/file_metadata" + type: path + method: [get, post] + } + allow: "*" + sort-order: 500 + name: "puppetlabs file metadata" + }, + { + # Allow nodes to retrieve only their own node definition + match-request: { + path: "^/puppet/v3/node/([^/]+)$" + type: regex + method: get + } + allow: "$1" + sort-order: 500 + name: "puppetlabs node" + }, + { + # Allow nodes to store only their own reports + match-request: { + path: "^/puppet/v3/report/([^/]+)$" + type: regex + method: put + } + allow: "$1" + sort-order: 500 + name: "puppetlabs report" + }, + { + # Allow nodes to update their own facts + match-request: { + path: "^/puppet/v3/facts/([^/]+)$" + type: regex + method: put + } + allow: "$1" + sort-order: 500 + name: "puppetlabs facts" + }, + { + match-request: { + path: "/puppet/v3/static_file_content" + type: path + method: get + } + allow: "*" + sort-order: 500 + name: "puppetlabs static file content" + }, + { + match-request: { + path: "/puppet/v3/tasks" + type: path + } + allow: "*" + sort-order: 500 + name: "puppet tasks information" + }, + { + match-request: { + path: "/puppet-admin-api/v1/environment-cache" + type: path + method: delete + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "primary can clean cache after code deploy" + }, + { + # Deny everything else. This ACL is not strictly + # necessary, but illustrates the default policy + match-request: { + path: "/" + type: path + } + deny: "*" + sort-order: 999 + name: "puppetlabs deny all" + } + ] +} diff --git a/packaging/resources/files/config/conf.d/ca.conf b/packaging/resources/files/config/conf.d/ca.conf new file mode 100644 index 000000000..11e84bb99 --- /dev/null +++ b/packaging/resources/files/config/conf.d/ca.conf @@ -0,0 +1,16 @@ +certificate-authority: { + # allow CA to sign certificate requests that have subject alternative names. + # allow-subject-alt-names: false + + # allow CA to sign certificate requests that have authorization extensions. + # allow-authorization-extensions: false + + # enable the separate CRL for Puppet infrastructure nodes + # enable-infra-crl: false + # Disable auto renewal of certs by default. + allow-auto-renewal: false + # This value determines the lifetime of the cert if auto-renewal is enabled + auto-renewal-cert-ttl: "60d" + # Default cert expiration time. If the value is set here, it will take precedence over ca-ttl setting in puppet.conf + #ca-ttl: "60d" +} diff --git a/packaging/resources/files/config/conf.d/global.conf b/packaging/resources/files/config/conf.d/global.conf new file mode 100644 index 000000000..ed76c439a --- /dev/null +++ b/packaging/resources/files/config/conf.d/global.conf @@ -0,0 +1,5 @@ +global: { + # Path to logback logging configuration file; for more + # info, see http://logback.qos.ch/manual/configuration.html + logging-config: /etc/puppetlabs/puppetserver/logback.xml +} diff --git a/packaging/resources/files/config/conf.d/metrics.conf b/packaging/resources/files/config/conf.d/metrics.conf new file mode 100644 index 000000000..0e03d50e1 --- /dev/null +++ b/packaging/resources/files/config/conf.d/metrics.conf @@ -0,0 +1,53 @@ +# settings related to metrics +metrics: { + # a server id that will be used as part of the namespace for metrics produced + # by this server + server-id: localhost + registries: { + puppetserver: { + # specify metrics to allow in addition to those in the default list + #metrics-allowed: ["compiler.compile.production"] + + reporters: { + # enable or disable JMX metrics reporter + jmx: { + enabled: true + } + # enable or disable Graphite metrics reporter + #graphite: { + # enabled: true + #} + } + + } + } + + # this section is used to configure settings for reporters that will send + # the metrics to various destinations for external viewing + reporters: { + #graphite: { + # # graphite host + # host: "127.0.0.1" + # # graphite metrics port + # port: 2003 + # # how often to send metrics to graphite + # update-interval-seconds: 5 + #} + } + metrics-webservice: { + jolokia: { + # Enable or disable the Jolokia-based metrics/v2 endpoint. + # Default is true. + # enabled: false + + # Configure any of the settings listed at: + # https://jolokia.org/reference/html/agents.html#war-agent-installation + servlet-init-params: { + # Specify a custom security policy: + # https://jolokia.org/reference/html/security.html + # policyLocation: "file:///etc/puppetlabs/puppetserver/jolokia-access.xml" + } + } + } + +} diff --git a/packaging/resources/files/config/conf.d/puppetserver.conf b/packaging/resources/files/config/conf.d/puppetserver.conf new file mode 100644 index 000000000..45866ad91 --- /dev/null +++ b/packaging/resources/files/config/conf.d/puppetserver.conf @@ -0,0 +1,74 @@ +# configuration for the JRuby interpreters +jruby-puppet: { + # Where the openvox-agent dependency places puppet, facter, etc... + # OpenVox Server expects to load OpenVox from this location + ruby-load-path: [/opt/puppetlabs/puppet/lib/ruby/vendor_ruby] + + # This setting determines where JRuby will install gems. It is used for loading gems, + # and also by the `puppetserver gem` command line tool. + gem-home: /opt/puppetlabs/server/data/puppetserver/jruby-gems + + # This setting defines the complete "GEM_PATH" for jruby. If set, it should include + # the gem-home directory as well as any other directories that gems can be loaded + # from (including the vendored gems directory for gems that ship with puppetserver) + gem-path: [${jruby-puppet.gem-home}, "/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems", "/opt/puppetlabs/puppet/lib/ruby/vendor_gems"] + + # PLEASE NOTE: Use caution when modifying the below settings. Modifying + # these settings will change the value of the corresponding OpenVox settings + # for OpenVox Server, but not for the OpenVox CLI tools. This likely will not + # be a problem with server-var-dir, server-run-dir, or server-log-dir unless + # some critical setting in puppet.conf is interpolating the value of one + # of the corresponding settings, but it is important that any changes made to + # server-conf-dir and server-code-dir are also made to the corresponding OpenVox + # settings when running the OpenVox CLI tools. See + # https://docs.openvoxproject.org/openvox-server/latest/config_file_puppetserver.html#settings + # for more information. + + # (optional) path to puppet conf dir; if not specified, will use + # /etc/puppetlabs/puppet + server-conf-dir: /etc/puppetlabs/puppet + + # (optional) path to puppet code dir; if not specified, will use + # /etc/puppetlabs/code + server-code-dir: /etc/puppetlabs/code + + # (optional) path to puppet var dir; if not specified, will use + # /opt/puppetlabs/server/data/puppetserver + server-var-dir: /opt/puppetlabs/server/data/puppetserver + + # (optional) path to puppet run dir; if not specified, will use + # /var/run/puppetlabs/puppetserver + server-run-dir: /var/run/puppetlabs/puppetserver + + # (optional) path to puppet log dir; if not specified, will use + # /var/log/puppetlabs/puppetserver + server-log-dir: /var/log/puppetlabs/puppetserver + + # (optional) maximum number of JRuby instances to allow + #max-active-instances: 1 + + # (optional) Whether or not to track lookups during compilation; turning + # this on will send that information to puppetdb + # track-lookups: true +} + +# settings related to HTTPS client requests made by OpenVox Server +http-client: { + # A list of acceptable protocols for making HTTPS requests + #ssl-protocols: [TLSv1.3, TLSv1.2] + + # A list of acceptable cipher suites for making HTTPS requests + #cipher-suites: [TLS_RSA_WITH_AES_256_CBC_SHA256, + # TLS_RSA_WITH_AES_256_CBC_SHA, + # TLS_RSA_WITH_AES_128_CBC_SHA256, + # TLS_RSA_WITH_AES_128_CBC_SHA] + + # Whether to enable http-client metrics; defaults to 'true'. + #metrics-enabled: true +} + +# settings related to profiling the puppet Ruby code +profiler: { + # enable or disable profiling for the Ruby code; defaults to 'true'. + #enabled: true +} diff --git a/packaging/resources/files/config/conf.d/web-routes.conf b/packaging/resources/files/config/conf.d/web-routes.conf new file mode 100644 index 000000000..43e97d393 --- /dev/null +++ b/packaging/resources/files/config/conf.d/web-routes.conf @@ -0,0 +1,16 @@ +web-router-service: { + # These two should not be modified because the Puppet 4.x agent expects them to + # be mounted at these specific paths + "puppetlabs.services.ca.certificate-authority-service/certificate-authority-service": "/puppet-ca" + "puppetlabs.services.master.master-service/master-service": "/puppet" + "puppetlabs.services.legacy-routes.legacy-routes-service/legacy-routes-service": "" + + # This controls the mount point for the puppet admin API. + "puppetlabs.services.puppet-admin.puppet-admin-service/puppet-admin-service": "/puppet-admin-api" + + # This controls the mount point for the status API + "puppetlabs.trapperkeeper.services.status.status-service/status-service": "/status" + + # This controls the mount point for the metrics API + "puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice": "/metrics" +} diff --git a/packaging/resources/files/config/conf.d/webserver.conf b/packaging/resources/files/config/conf.d/webserver.conf new file mode 100644 index 000000000..1f541e745 --- /dev/null +++ b/packaging/resources/files/config/conf.d/webserver.conf @@ -0,0 +1,6 @@ +webserver: { + access-log-config: /etc/puppetlabs/puppetserver/request-logging.xml + client-auth: want + ssl-host: 0.0.0.0 + ssl-port: 8140 +} diff --git a/packaging/resources/files/config/logback.xml b/packaging/resources/files/config/logback.xml new file mode 100644 index 000000000..d8a5ceed4 --- /dev/null +++ b/packaging/resources/files/config/logback.xml @@ -0,0 +1,57 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n + + + + + + /var/log/puppetlabs/puppetserver/puppetserver.log + true + + + /var/log/puppetlabs/puppetserver/puppetserver-%d{yyyy-MM-dd}.%i.log.gz + + 200MB + 90 + 1GB + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n + + + + + /var/log/puppetlabs/puppetserver/puppetserver-status.log + true + + + /var/log/puppetlabs/puppetserver/puppetserver-status-%d{yyyy-MM-dd}.%i.log.gz + + 200MB + 90 + 1GB + + + + %m%n + + + + + + + + + + + + + + + + + + + diff --git a/packaging/resources/files/config/request-logging.xml b/packaging/resources/files/config/request-logging.xml new file mode 100644 index 000000000..46fff776f --- /dev/null +++ b/packaging/resources/files/config/request-logging.xml @@ -0,0 +1,18 @@ + + + /var/log/puppetlabs/puppetserver/puppetserver-access.log + true + + + /var/log/puppetlabs/puppetserver/puppetserver-access-%d{yyyy-MM-dd}.%i.log.gz + + 200MB + 90 + 1GB + + + %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}" %D %i{Content-Length} %mdc{jruby.borrow-time:--} + + + + diff --git a/packaging/resources/files/config/services.d/ca.cfg b/packaging/resources/files/config/services.d/ca.cfg new file mode 100644 index 000000000..e3c1c848b --- /dev/null +++ b/packaging/resources/files/config/services.d/ca.cfg @@ -0,0 +1,6 @@ +# To enable the CA service, leave the following line uncommented +puppetlabs.services.ca.certificate-authority-service/certificate-authority-service +# To disable the CA service, comment out the above line and uncomment the line below +#puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service +puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service + diff --git a/packaging/resources/files/java.security.fips b/packaging/resources/files/java.security.fips new file mode 100644 index 000000000..ac046add0 --- /dev/null +++ b/packaging/resources/files/java.security.fips @@ -0,0 +1,1098 @@ +# +# This is the "master security properties file". +# +# An alternate java.security properties file may be specified +# from the command line via the system property +# +# -Djava.security.properties= +# +# This properties file appends to the master security properties file. +# If both properties files specify values for the same key, the value +# from the command-line properties file is selected, as it is the last +# one loaded. +# +# Also, if you specify +# +# -Djava.security.properties== (2 equals), +# +# then that properties file completely overrides the master security +# properties file. +# +# To disable the ability to specify an additional properties file from +# the command line, set the key security.overridePropertiesFile +# to false in the master security properties file. It is set to true +# by default. + +# In this file, various security properties are set for use by +# java.security classes. This is where users can statically register +# Cryptography Package Providers ("providers" for short). The term +# "provider" refers to a package or set of packages that supply a +# concrete implementation of a subset of the cryptography aspects of +# the Java Security API. A provider may, for example, implement one or +# more digital signature algorithms or message digest algorithms. +# +# Each provider must implement a subclass of the Provider class. +# To register a provider in this master security properties file, +# specify the provider and priority in the format +# +# security.provider.= +# +# This declares a provider, and specifies its preference +# order n. The preference order is the order in which providers are +# searched for requested algorithms (when no specific provider is +# requested). The order is 1-based; 1 is the most preferred, followed +# by 2, and so on. +# +# must specify the name of the Provider as passed to its super +# class java.security.Provider constructor. This is for providers loaded +# through the ServiceLoader mechanism. +# +# must specify the subclass of the Provider class whose +# constructor sets the values of various properties that are required +# for the Java Security API to look up the algorithms or other +# facilities implemented by the provider. This is for providers loaded +# through classpath. +# +# Note: Providers can be dynamically registered instead by calls to +# either the addProvider or insertProviderAt method in the Security +# class. + +# +# List of providers and their preference orders (see above): +# +security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider +security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider +security.provider.3=SUN +security.provider.4=SunRsaSign +#security.provider.5=SunEC +#security.provider.6=SunJSSE +#security.provider.7=SunJCE +#security.provider.8=SunJGSS +#security.provider.9=SunSASL +#security.provider.10=XMLDSig +#security.provider.11=SunPCSC +#security.provider.12=JdkLDAP +#security.provider.13=JdkSASL +#security.provider.14=SunPKCS11 + +# +# A list of preferred providers for specific algorithms. These providers will +# be searched for matching algorithms before the list of registered providers. +# Entries containing errors (parsing, etc) will be ignored. Use the +# -Djava.security.debug=jca property to debug these errors. +# +# The property is a comma-separated list of serviceType.algorithm:provider +# entries. The serviceType (example: "MessageDigest") is optional, and if +# not specified, the algorithm applies to all service types that support it. +# The algorithm is the standard algorithm name or transformation. +# Transformations can be specified in their full standard name +# (ex: AES/CBC/PKCS5Padding), or as partial matches (ex: AES, AES/CBC). +# The provider is the name of the provider. Any provider that does not +# also appear in the registered list will be ignored. +# +# There is a special serviceType for this property only to group a set of +# algorithms together. The type is "Group" and is followed by an algorithm +# keyword. Groups are to simplify and lessen the entries on the property +# line. Current groups are: +# Group.SHA2 = SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 +# Group.HmacSHA2 = HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512 +# Group.SHA2RSA = SHA224withRSA, SHA256withRSA, SHA384withRSA, SHA512withRSA +# Group.SHA2DSA = SHA224withDSA, SHA256withDSA, SHA384withDSA, SHA512withDSA +# Group.SHA2ECDSA = SHA224withECDSA, SHA256withECDSA, SHA384withECDSA, \ +# SHA512withECDSA +# Group.SHA3 = SHA3-224, SHA3-256, SHA3-384, SHA3-512 +# Group.HmacSHA3 = HmacSHA3-224, HmacSHA3-256, HmacSHA3-384, HmacSHA3-512 +# +# Example: +# jdk.security.provider.preferred=AES/GCM/NoPadding:SunJCE, \ +# MessageDigest.SHA-256:SUN, Group.HmacSHA2:SunJCE +# +#jdk.security.provider.preferred= + + +# +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "NativePRNG", "SHA1PRNG" +# and "DRBG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG", "SHA1PRNG" and "DRBG" implementations obtains seed data from +# special device files such as file:/dev/random. +# +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG and DRBG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. +# +# SHA1PRNG and DRBG: +# the traditional system/thread activity algorithm will be used. +# +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass +# +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# DRBG and SHA1PRNG in the Sun provider. +# +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN + +# +# Sun provider DRBG configuration and default instantiation request. +# +# NIST SP 800-90Ar1 lists several DRBG mechanisms. Each can be configured +# with a DRBG algorithm name, and can be instantiated with a security strength, +# prediction resistance support, etc. This property defines the configuration +# and the default instantiation request of "DRBG" SecureRandom implementations +# in the SUN provider. (Other DRBG implementations can also use this property.) +# Applications can request different instantiation parameters like security +# strength, capability, personalization string using one of the +# getInstance(...,SecureRandomParameters,...) methods with a +# DrbgParameters.Instantiation argument, but other settings such as the +# mechanism and DRBG algorithm names are not currently configurable by any API. +# +# Please note that the SUN implementation of DRBG always supports reseeding. +# +# The value of this property is a comma-separated list of all configurable +# aspects. The aspects can appear in any order but the same aspect can only +# appear at most once. Its BNF-style definition is: +# +# Value: +# aspect { "," aspect } +# +# aspect: +# mech_name | algorithm_name | strength | capability | df +# +# // The DRBG mechanism to use. Default "Hash_DRBG" +# mech_name: +# "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG" +# +# // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and +# // HMAC_DRBG, default "SHA-256". The "AES-***" names are for CTR_DRBG, +# // default "AES-128" when using the limited cryptographic or "AES-256" +# // when using the unlimited. +# algorithm_name: +# "SHA-224" | "SHA-512/224" | "SHA-256" | +# "SHA-512/256" | "SHA-384" | "SHA-512" | +# "AES-128" | "AES-192" | "AES-256" +# +# // Security strength requested. Default "128" +# strength: +# "112" | "128" | "192" | "256" +# +# // Prediction resistance and reseeding request. Default "none" +# // "pr_and_reseed" - Both prediction resistance and reseeding +# // support requested +# // "reseed_only" - Only reseeding support requested +# // "none" - Neither prediction resistance not reseeding +# // support requested +# pr: +# "pr_and_reseed" | "reseed_only" | "none" +# +# // Whether a derivation function should be used. only applicable +# // to CTR_DRBG. Default "use_df" +# df: +# "use_df" | "no_df" +# +# Examples, +# securerandom.drbg.config=Hash_DRBG,SHA-224,112,none +# securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df +# +# The default value is an empty string, which is equivalent to +# securerandom.drbg.config=Hash_DRBG,SHA-256,128,none +# +securerandom.drbg.config= + +# +# Class to instantiate as the javax.security.auth.login.Configuration +# provider. +# +login.configuration.provider=sun.security.provider.ConfigFile + +# +# Default login configuration file +# +#login.config.url.1=file:${user.home}/.java.login.config + +# +# Class to instantiate as the system Policy. This is the name of the class +# that will be used as the Policy object. The system class loader is used to +# locate this class. +# +policy.provider=sun.security.provider.PolicyFile + +# The default is to have a single system-wide policy file, +# and a policy file in the user's home directory. +# +policy.url.1=file:${java.home}/conf/security/java.policy +policy.url.2=file:${user.home}/.java.policy + +# whether or not we expand properties in the policy file +# if this is set to false, properties (${...}) will not be expanded in policy +# files. +# +policy.expandProperties=true + +# whether or not we allow an extra policy to be passed on the command line +# with -Djava.security.policy=somefile. Comment out this line to disable +# this feature. +# +policy.allowSystemProperty=true + +# whether or not we look into the IdentityScope for trusted Identities +# when encountering a 1.1 signed JAR file. If the identity is found +# and is trusted, we grant it AllPermission. Note: the default policy +# provider (sun.security.provider.PolicyFile) does not support this property. +# +policy.ignoreIdentityScope=false + +# +# Default keystore type. +# +keystore.type=pkcs12 + +# +# Controls compatibility mode for JKS and PKCS12 keystore types. +# +# When set to 'true', both JKS and PKCS12 keystore types support loading +# keystore files in either JKS or PKCS12 format. When set to 'false' the +# JKS keystore type supports loading only JKS keystore files and the PKCS12 +# keystore type supports loading only PKCS12 keystore files. +# +keystore.type.compat=true + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when passed to the +# SecurityManager::checkPackageAccess method unless the corresponding +# RuntimePermission("accessClassInPackage."+package) has been granted. +# +package.access=sun.misc.,\ + sun.reflect.,\ + org.GNOME.Accessibility. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when passed to the +# SecurityManager::checkPackageDefinition method unless the corresponding +# RuntimePermission("defineClassInPackage."+package) has been granted. +# +# By default, none of the class loaders supplied with the JDK call +# checkPackageDefinition. +# +package.definition=sun.misc.,\ + sun.reflect. + +# +# Determines whether this properties file can be appended to +# or overridden on the command line via -Djava.security.properties +# +security.overridePropertiesFile=true + +# +# Determines the default key and trust manager factory algorithms for +# the javax.net.ssl package. +# +ssl.KeyManagerFactory.algorithm=BCFKS +ssl.TrustManagerFactory.algorithm=PKIX + +# +# The Java-level namelookup cache policy for successful lookups: +# +# any negative value: caching forever +# any positive value: the number of seconds to cache an address for +# zero: do not cache +# +# default value is forever (FOREVER). For security reasons, this +# caching is made forever when a security manager is set. When a security +# manager is not set, the default behavior in this implementation +# is to cache for 30 seconds. +# +# NOTE: setting this to anything other than the default value can have +# serious security implications. Do not set it unless +# you are sure you are not exposed to DNS spoofing attack. +# +#networkaddress.cache.ttl=-1 + +# The Java-level namelookup cache policy for failed lookups: +# +# any negative value: cache forever +# any positive value: the number of seconds to cache negative lookup results +# zero: do not cache +# +# In some Microsoft Windows networking environments that employ +# the WINS name service in addition to DNS, name service lookups +# that fail may take a noticeably long time to return (approx. 5 seconds). +# For this reason the default caching policy is to maintain these +# results for 10 seconds. +# +networkaddress.cache.negative.ttl=10 + +# +# Properties to configure OCSP for certificate revocation checking +# + +# Enable OCSP +# +# By default, OCSP is not used for certificate revocation checking. +# This property enables the use of OCSP when set to the value "true". +# +# NOTE: SocketPermission is required to connect to an OCSP responder. +# +# Example, +# ocsp.enable=true + +# +# Location of the OCSP responder +# +# By default, the location of the OCSP responder is determined implicitly +# from the certificate being validated. This property explicitly specifies +# the location of the OCSP responder. The property is used when the +# Authority Information Access extension (defined in RFC 5280) is absent +# from the certificate or when it requires overriding. +# +# Example, +# ocsp.responderURL=http://ocsp.example.net:80 + +# +# Subject name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. In cases where +# the subject name alone is not sufficient to uniquely identify the certificate +# then both the "ocsp.responderCertIssuerName" and +# "ocsp.responderCertSerialNumber" properties must be used instead. When this +# property is set then those two properties are ignored. +# +# Example, +# ocsp.responderCertSubjectName=CN=OCSP Responder, O=XYZ Corp + +# +# Issuer name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. When this +# property is set then the "ocsp.responderCertSerialNumber" property must also +# be set. When the "ocsp.responderCertSubjectName" property is set then this +# property is ignored. +# +# Example, +# ocsp.responderCertIssuerName=CN=Enterprise CA, O=XYZ Corp + +# +# Serial number of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# of hexadecimal digits (colon or space separators may be present) which +# identifies a certificate in the set of certificates supplied during cert path +# validation. When this property is set then the "ocsp.responderCertIssuerName" +# property must also be set. When the "ocsp.responderCertSubjectName" property +# is set then this property is ignored. +# +# Example, +# ocsp.responderCertSerialNumber=2A:FF:00 + +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +# +krb5.kdc.bad.policy = tryLast + +# +# Algorithm restrictions for certification path (CertPath) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# for certification path building and validation. For example, "MD2" is +# generally no longer considered to be a secure hash algorithm. This section +# describes the mechanism for disabling algorithms based on algorithm name +# and/or key length. This includes algorithms used in certificates, as well +# as revocation information such as CRLs and signed OCSP Responses. +# The syntax of the disabled algorithm string is described as follows: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] { '&' Constraint } +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint | CAConstraint | DenyAfterConstraint | +# UsageConstraint +# +# KeySizeConstraint: +# keySize Operator KeyLength +# +# Operator: +# <= | < | == | != | >= | > +# +# KeyLength: +# Integer value of the algorithm's key length in bits +# +# CAConstraint: +# jdkCA +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# +# UsageConstraint: +# usage [TLSServer] [TLSClient] [SignedJAR] +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# algorithm. See "Java Cryptography Architecture Standard Algorithm Name +# Documentation" for information about Standard Algorithm Names. Matching +# is performed using a case-insensitive sub-element matching rule. (For +# example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and +# "ECDSA" for signatures.) If the assertion "AlgorithmName" is a +# sub-element of the certificate algorithm name, the algorithm will be +# rejected during certification path building and validation. For example, +# the assertion algorithm name "DSA" will disable all certificate algorithms +# that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion +# will not disable algorithms related to "ECDSA". +# +# A "Constraint" defines restrictions on the keys and/or certificates for +# a specified AlgorithmName: +# +# KeySizeConstraint: +# keySize Operator KeyLength +# The constraint requires a key of a valid size range if the +# "AlgorithmName" is of a key algorithm. The "KeyLength" indicates +# the key size specified in number of bits. For example, +# "RSA keySize <= 1024" indicates that any RSA key with key size less +# than or equal to 1024 bits should be disabled, and +# "RSA keySize < 1024, RSA keySize > 2048" indicates that any RSA key +# with key size less than 1024 or greater than 2048 should be disabled. +# This constraint is only used on algorithms that have a key size. +# +# CAConstraint: +# jdkCA +# This constraint prohibits the specified algorithm only if the +# algorithm is used in a certificate chain that terminates at a marked +# trust anchor in the lib/security/cacerts keystore. If the jdkCA +# constraint is not set, then all chains using the specified algorithm +# are restricted. jdkCA may only be used once in a DisabledAlgorithm +# expression. +# Example: To apply this constraint to SHA-1 certificates, include +# the following: "SHA1 jdkCA" +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# This constraint prohibits a certificate with the specified algorithm +# from being used after the date regardless of the certificate's +# validity. JAR files that are signed and timestamped before the +# constraint date with certificates containing the disabled algorithm +# will not be restricted. The date is processed in the UTC timezone. +# This constraint can only be used once in a DisabledAlgorithm +# expression. +# Example: To deny usage of RSA 2048 bit certificates after Feb 3 2020, +# use the following: "RSA keySize == 2048 & denyAfter 2020-02-03" +# +# UsageConstraint: +# usage [TLSServer] [TLSClient] [SignedJAR] +# This constraint prohibits the specified algorithm for +# a specified usage. This should be used when disabling an algorithm +# for all usages is not practical. 'TLSServer' restricts the algorithm +# in TLS server certificate chains when server authentication is +# performed. 'TLSClient' restricts the algorithm in TLS client +# certificate chains when client authentication is performed. +# 'SignedJAR' constrains use of certificates in signed jar files. +# The usage type follows the keyword and more than one usage type can +# be specified with a whitespace delimiter. +# Example: "SHA1 usage TLSServer TLSClient" +# +# When an algorithm must satisfy more than one constraint, it must be +# delimited by an ampersand '&'. For example, to restrict certificates in a +# chain that terminate at a distribution provided trust anchor and contain +# RSA keys that are less than or equal to 1024 bits, add the following +# constraint: "RSA keySize <= 1024 & jdkCA". +# +# All DisabledAlgorithms expressions are processed in the order defined in the +# property. This requires lower keysize constraints to be specified +# before larger keysize constraints of the same algorithm. For example: +# "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". +# +# Note: The algorithm restrictions do not apply to trust anchors or +# self-signed certificates. +# +# Note: This property is currently used by Oracle's PKIX implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 +# +# +jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \ + RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224 + +# +# Algorithm restrictions for signed JAR files +# +# In some environments, certain algorithms or key lengths may be undesirable +# for signed JAR validation. For example, "MD2" is generally no longer +# considered to be a secure hash algorithm. This section describes the +# mechanism for disabling algorithms based on algorithm name and/or key length. +# JARs signed with any of the disabled algorithms or key sizes will be treated +# as unsigned. +# +# The syntax of the disabled algorithm string is described as follows: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] { '&' Constraint } +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint | DenyAfterConstraint +# +# KeySizeConstraint: +# keySize Operator KeyLength +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# +# Operator: +# <= | < | == | != | >= | > +# +# KeyLength: +# Integer value of the algorithm's key length in bits +# +# Note: This property is currently used by the JDK Reference +# implementation. It is not guaranteed to be examined and used by other +# implementations. +# +# See "jdk.certpath.disabledAlgorithms" for syntax descriptions. +# +jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ + DSA keySize < 1024 + +# +# Algorithm restrictions for Secure Socket Layer/Transport Layer Security +# (SSL/TLS/DTLS) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# when using SSL/TLS/DTLS. This section describes the mechanism for disabling +# algorithms during SSL/TLS/DTLS security parameters negotiation, including +# protocol version negotiation, cipher suites selection, peer authentication +# and key exchange mechanisms. +# +# Disabled algorithms will not be negotiated for SSL/TLS connections, even +# if they are enabled explicitly in an application. +# +# For PKI-based peer authentication and key exchange mechanisms, this list +# of disabled algorithms will also be checked during certification path +# building and validation, including algorithms used in certificates, as +# well as revocation information such as CRLs and signed OCSP Responses. +# This is in addition to the jdk.certpath.disabledAlgorithms property above. +# +# See the specification of "jdk.certpath.disabledAlgorithms" for the +# syntax of the disabled algorithm string. +# +# Note: The algorithm restrictions do not apply to trust anchors or +# self-signed certificates. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 +jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \ + EC keySize < 224, 3DES_EDE_CBC, anon, NULL + +# +# Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) +# processing in JSSE implementation. +# +# In some environments, a certain algorithm may be undesirable but it +# cannot be disabled because of its use in legacy applications. Legacy +# algorithms may still be supported, but applications should not use them +# as the security strength of legacy algorithms are usually not strong enough +# in practice. +# +# During SSL/TLS security parameters negotiation, legacy algorithms will +# not be negotiated unless there are no other candidates. +# +# The syntax of the legacy algorithms string is described as this Java +# BNF-style: +# LegacyAlgorithms: +# " LegacyAlgorithm { , LegacyAlgorithm } " +# +# LegacyAlgorithm: +# AlgorithmName (standard JSSE algorithm name) +# +# See the specification of security property "jdk.certpath.disabledAlgorithms" +# for the syntax and description of the "AlgorithmName" notation. +# +# Per SSL/TLS specifications, cipher suites have the form: +# SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# or +# TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# +# For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the +# key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC +# mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest +# algorithm for HMAC. +# +# The LegacyAlgorithm can be one of the following standard algorithm names: +# 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA +# 2. JSSE key exchange algorithm name, e.g., RSA +# 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC +# 4. JSSE message digest algorithm name, e.g., SHA +# +# See SSL/TLS specifications and "Java Cryptography Architecture Standard +# Algorithm Name Documentation" for information about the algorithm names. +# +# Note: If a legacy algorithm is also restricted through the +# jdk.tls.disabledAlgorithms property or the +# java.security.AlgorithmConstraints API (See +# javax.net.ssl.SSLParameters.setAlgorithmConstraints()), +# then the algorithm is completely disabled and will not be negotiated. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# There is no guarantee the property will continue to exist or be of the +# same syntax in future releases. +# +# Example: +# jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 +# +jdk.tls.legacyAlgorithms= \ + K_NULL, C_NULL, M_NULL, \ + DH_anon, ECDH_anon, \ + RC4_128, RC4_40, DES_CBC, DES40_CBC, \ + 3DES_EDE_CBC + +# +# The pre-defined default finite field Diffie-Hellman ephemeral (DHE) +# parameters for Transport Layer Security (SSL/TLS/DTLS) processing. +# +# In traditional SSL/TLS/DTLS connections where finite field DHE parameters +# negotiation mechanism is not used, the server offers the client group +# parameters, base generator g and prime modulus p, for DHE key exchange. +# It is recommended to use dynamic group parameters. This property defines +# a mechanism that allows you to specify custom group parameters. +# +# The syntax of this property string is described as this Java BNF-style: +# DefaultDHEParameters: +# DefinedDHEParameters { , DefinedDHEParameters } +# +# DefinedDHEParameters: +# "{" DHEPrimeModulus , DHEBaseGenerator "}" +# +# DHEPrimeModulus: +# HexadecimalDigits +# +# DHEBaseGenerator: +# HexadecimalDigits +# +# HexadecimalDigits: +# HexadecimalDigit { HexadecimalDigit } +# +# HexadecimalDigit: one of +# 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f +# +# Whitespace characters are ignored. +# +# The "DefinedDHEParameters" defines the custom group parameters, prime +# modulus p and base generator g, for a particular size of prime modulus p. +# The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the +# "DHEBaseGenerator" defines the hexadecimal base generator g of a group +# parameter. It is recommended to use safe primes for the custom group +# parameters. +# +# If this property is not defined or the value is empty, the underlying JSSE +# provider's default group parameter is used for each connection. +# +# If the property value does not follow the grammar, or a particular group +# parameter is not valid, the connection will fall back and use the +# underlying JSSE provider's default group parameter. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.server.defaultDHEParameters= +# { \ +# FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \ +# 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \ +# EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \ +# E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \ +# EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \ +# FFFFFFFF FFFFFFFF, 2} + +# +# TLS key limits on symmetric cryptographic algorithms +# +# This security property sets limits on algorithms key usage in TLS 1.3. +# When the amount of data encrypted exceeds the algorithm value listed below, +# a KeyUpdate message will trigger a key change. This is for symmetric ciphers +# with TLS 1.3 only. +# +# The syntax for the property is described below: +# KeyLimits: +# " KeyLimit { , KeyLimit } " +# +# WeakKeyLimit: +# AlgorithmName Action Length +# +# AlgorithmName: +# A full algorithm transformation. +# +# Action: +# KeyUpdate +# +# Length: +# The amount of encrypted data in a session before the Action occurs +# This value may be an integer value in bytes, or as a power of two, 2^29. +# +# KeyUpdate: +# The TLS 1.3 KeyUpdate handshake process begins when the Length amount +# is fulfilled. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 + +# +# Cryptographic Jurisdiction Policy defaults +# +# Import and export control rules on cryptographic software vary from +# country to country. By default, Java provides two different sets of +# cryptographic policy files[1]: +# +# unlimited: These policy files contain no restrictions on cryptographic +# strengths or algorithms +# +# limited: These policy files contain more restricted cryptographic +# strengths +# +# The default setting is determined by the value of the "crypto.policy" +# Security property below. If your country or usage requires the +# traditional restrictive policy, the "limited" Java cryptographic +# policy is still available and may be appropriate for your environment. +# +# If you have restrictions that do not fit either use case mentioned +# above, Java provides the capability to customize these policy files. +# The "crypto.policy" security property points to a subdirectory +# within /conf/security/policy/ which can be customized. +# Please see the /conf/security/policy/README.txt file or consult +# the Java Security Guide/JCA documentation for more information. +# +# YOU ARE ADVISED TO CONSULT YOUR EXPORT/IMPORT CONTROL COUNSEL OR ATTORNEY +# TO DETERMINE THE EXACT REQUIREMENTS. +# +# [1] Please note that the JCE for Java SE, including the JCE framework, +# cryptographic policy files, and standard JCE providers provided with +# the Java SE, have been reviewed and approved for export as mass market +# encryption item by the US Bureau of Industry and Security. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +crypto.policy=unlimited + +# +# The policy for the XML Signature secure validation mode. The mode is +# enabled by setting the property "org.jcp.xml.dsig.secureValidation" to +# true with the javax.xml.crypto.XMLCryptoContext.setProperty() method, +# or by running the code with a SecurityManager. +# +# Policy: +# Constraint {"," Constraint } +# Constraint: +# AlgConstraint | MaxTransformsConstraint | MaxReferencesConstraint | +# ReferenceUriSchemeConstraint | KeySizeConstraint | OtherConstraint +# AlgConstraint +# "disallowAlg" Uri +# MaxTransformsConstraint: +# "maxTransforms" Integer +# MaxReferencesConstraint: +# "maxReferences" Integer +# ReferenceUriSchemeConstraint: +# "disallowReferenceUriSchemes" String { String } +# KeySizeConstraint: +# "minKeySize" KeyAlg Integer +# OtherConstraint: +# "noDuplicateIds" | "noRetrievalMethodLoops" +# +# For AlgConstraint, Uri is the algorithm URI String that is not allowed. +# See the XML Signature Recommendation for more information on algorithm +# URI Identifiers. For KeySizeConstraint, KeyAlg is the standard algorithm +# name of the key type (ex: "RSA"). If the MaxTransformsConstraint, +# MaxReferencesConstraint or KeySizeConstraint (for the same key type) is +# specified more than once, only the last entry is enforced. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.xml.dsig.secureValidationPolicy=\ + disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ + maxTransforms 5,\ + maxReferences 30,\ + disallowReferenceUriSchemes file http https,\ + minKeySize RSA 1024,\ + minKeySize DSA 1024,\ + minKeySize EC 224,\ + noDuplicateIds,\ + noRetrievalMethodLoops + +# +# Serialization process-wide filter +# +# A filter, if configured, is used by java.io.ObjectInputStream during +# deserialization to check the contents of the stream. +# A filter is configured as a sequence of patterns, each pattern is either +# matched against the name of a class in the stream or defines a limit. +# Patterns are separated by ";" (semicolon). +# Whitespace is significant and is considered part of the pattern. +# +# If the system property jdk.serialFilter is also specified, it supersedes +# the security property value defined here. +# +# If a pattern includes a "=", it sets a limit. +# If a limit appears more than once the last value is used. +# Limits are checked before classes regardless of the order in the +# sequence of patterns. +# If any of the limits are exceeded, the filter status is REJECTED. +# +# maxdepth=value - the maximum depth of a graph +# maxrefs=value - the maximum number of internal references +# maxbytes=value - the maximum number of bytes in the input stream +# maxarray=value - the maximum array length allowed +# +# Other patterns, from left to right, match the class or package name as +# returned from Class.getName. +# If the class is an array type, the class or package to be matched is the +# element type. +# Arrays of any number of dimensions are treated the same as the element type. +# For example, a pattern of "!example.Foo", rejects creation of any instance or +# array of example.Foo. +# +# If the pattern starts with "!", the status is REJECTED if the remaining +# pattern is matched; otherwise the status is ALLOWED if the pattern matches. +# If the pattern contains "/", the non-empty prefix up to the "/" is the +# module name; +# if the module name matches the module name of the class then +# the remaining pattern is matched with the class name. +# If there is no "/", the module name is not compared. +# If the pattern ends with ".**" it matches any class in the package and all +# subpackages. +# If the pattern ends with ".*" it matches any class in the package. +# If the pattern ends with "*", it matches any class with the pattern as a +# prefix. +# If the pattern is equal to the class name, it matches. +# Otherwise, the status is UNDECIDED. +# +#jdk.serialFilter=pattern;pattern + +# +# RMI Registry Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI Registry or to decrease limits but not +# to increase limits. +# If the limits (maxdepth, maxrefs, or maxbytes) are exceeded, the object is rejected. +# +# Each non-array type is allowed or rejected if it matches one of the patterns, +# evaluated from left to right, and is otherwise allowed. Arrays of any +# component type, including subarrays and arrays of primitives, are allowed. +# +# Array construction of any component type, including subarrays and arrays of +# primitives, are allowed unless the length is greater than the maxarray limit. +# The filter is applied to each array element. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# The built-in filter allows subclasses of allowed classes and +# can approximately be represented as the pattern: +# +#sun.rmi.registry.registryFilter=\ +# maxarray=1000000;\ +# maxdepth=20;\ +# java.lang.String;\ +# java.lang.Number;\ +# java.lang.reflect.Proxy;\ +# java.rmi.Remote;\ +# sun.rmi.server.UnicastRef;\ +# sun.rmi.server.RMIClientSocketFactory;\ +# sun.rmi.server.RMIServerSocketFactory;\ +# java.rmi.activation.ActivationID;\ +# java.rmi.server.UID +# +# RMI Distributed Garbage Collector (DGC) Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI DGC. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# The builtin DGC filter can approximately be represented as the filter pattern: +# +#sun.rmi.transport.dgcFilter=\ +# java.rmi.server.ObjID;\ +# java.rmi.server.UID;\ +# java.rmi.dgc.VMID;\ +# java.rmi.dgc.Lease;\ +# maxdepth=5;maxarray=10000 + +# CORBA ORBIorTypeCheckRegistryFilter +# Type check enhancement for ORB::string_to_object processing +# +# An IOR type check filter, if configured, is used by an ORB during +# an ORB::string_to_object invocation to check the veracity of the type encoded +# in the ior string. +# +# The filter pattern consists of a semi-colon separated list of class names. +# The configured list contains the binary class names of the IDL interface types +# corresponding to the IDL stub class to be instantiated. +# As such, a filter specifies a list of IDL stub classes that will be +# allowed by an ORB when an ORB::string_to_object is invoked. +# It is used to specify a white list configuration of acceptable +# IDL stub types which may be contained in a stringified IOR +# parameter passed as input to an ORB::string_to_object method. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +#com.sun.CORBA.ORBIorTypeCheckRegistryFilter=binary_class_name;binary_class_name + +# +# JCEKS Encrypted Key Serial Filter +# +# This filter, if configured, is used by the JCEKS KeyStore during the +# deserialization of the encrypted Key object stored inside a key entry. +# If not configured or the filter result is UNDECIDED (i.e. none of the patterns +# matches), the filter configured by jdk.serialFilter will be consulted. +# +# If the system property jceks.key.serialFilter is also specified, it supersedes +# the security property value defined here. +# +# The filter pattern uses the same format as jdk.serialFilter. The default +# pattern allows java.lang.Enum, java.security.KeyRep, java.security.KeyRep$Type, +# and javax.crypto.spec.SecretKeySpec and rejects all the others. +jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\ + java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!* + +# +# Enhanced exception message information +# +# By default, exception messages should not include potentially sensitive +# information such as file names, host names, or port numbers. This property +# accepts one or more comma separated values, each of which represents a +# category of enhanced exception message information to enable. Values are +# case-insensitive. Leading and trailing whitespaces, surrounding each value, +# are ignored. Unknown values are ignored. +# +# NOTE: Use caution before setting this property. Setting this property +# exposes sensitive information in Exceptions, which could, for example, +# propagate to untrusted code or be emitted in stack traces that are +# inadvertently disclosed and made accessible over a public network. +# +# The categories are: +# +# hostInfo - IOExceptions thrown by java.net.Socket and the socket types in the +# java.nio.channels package will contain enhanced exception +# message information +# +# The property setting in this file can be overridden by a system property of +# the same name, with the same syntax and possible values. +# +#jdk.includeInExceptions=hostInfo + +# +# Policies for distrusting Certificate Authorities (CAs). +# +# This is a comma separated value of one or more case-sensitive strings, each +# of which represents a policy for determining if a CA should be distrusted. +# The supported values are: +# +# SYMANTEC_TLS : Distrust TLS Server certificates anchored by a Symantec +# root CA and issued after April 16, 2019 unless issued by one of the +# following subordinate CAs which have a later distrust date: +# 1. Apple IST CA 2 - G1, SHA-256 fingerprint: +# AC2B922ECFD5E01711772FEA8ED372DE9D1E2245FCE3F57A9CDBEC77296A424B +# Distrust after December 31, 2019. +# 2. Apple IST CA 8 - G1, SHA-256 fingerprint: +# A4FE7C7F15155F3F0AEF7AAA83CF6E06DEB97CA3F909DF920AC1490882D488ED +# Distrust after December 31, 2019. +# +# Leading and trailing whitespace surrounding each value are ignored. +# Unknown values are ignored. If the property is commented out or set to the +# empty String, no policies are enforced. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be supported by other SE implementations. Also, this +# property does not override other security properties which can restrict +# certificates such as jdk.tls.disabledAlgorithms or +# jdk.certpath.disabledAlgorithms; those restrictions are still enforced even +# if this property is not enabled. +# +jdk.security.caDistrustPolicies=SYMANTEC_TLS + diff --git a/packaging/resources/files/system-config/services.d/bootstrap.cfg b/packaging/resources/files/system-config/services.d/bootstrap.cfg new file mode 100644 index 000000000..5f4e91251 --- /dev/null +++ b/packaging/resources/files/system-config/services.d/bootstrap.cfg @@ -0,0 +1,16 @@ +puppetlabs.services.request-handler.request-handler-service/request-handler-service +puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service +puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service +puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service +puppetlabs.trapperkeeper.services.webserver.jetty-service/jetty-service +puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service +puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service +puppetlabs.services.master.master-service/master-service +puppetlabs.services.puppet-admin.puppet-admin-service/puppet-admin-service +puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service +puppetlabs.services.versioned-code-service.versioned-code-service/versioned-code-service +puppetlabs.trapperkeeper.services.scheduler.scheduler-service/scheduler-service +puppetlabs.trapperkeeper.services.status.status-service/status-service +puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-service +puppetlabs.trapperkeeper.services.metrics.metrics-service/metrics-webservice +puppetlabs.services.jruby.jruby-metrics-service/jruby-metrics-service diff --git a/packaging/resources/files/wrapper/helper-functions.sh b/packaging/resources/files/wrapper/helper-functions.sh new file mode 100755 index 000000000..1348c3f9d --- /dev/null +++ b/packaging/resources/files/wrapper/helper-functions.sh @@ -0,0 +1,163 @@ +#! /bin/bash +# +# Useful shell functions. Identical to ezbake-functions.sh from when we +# were building with ezbake. +# + +# +# Wait `timeout` seconds for the application identified by `pid to bind to any +# TCP port +# +wait_for_app() +{ + local pid=${1:?} + local timeout=${2:-300} + + while : ;do + + # verify the process is still running; if not, return failure + ps -p $pid 2>&1 > /dev/null + if [ "$?" != 0 ]; then + return 1 + fi + + # if there are any TCP ports associated with the process, return success + netstat -tulpn 2>/dev/null | grep "$pid" 2>&1 >/dev/null + if [ "$?" = 0 ]; then + sleep 5 + return 0 + fi + + # if we reach the timeout, return failure + if [ "$timeout" = 0 ]; then + return 1 + fi + + sleep 1 + timeout=$(($timeout-1)) + done +} + + +# +# Wait `timeout` seconds for `pidfile` to be created, otherwise return failure. +# Default timeout is 5 seconds. +# +wait_for_pidfile() +{ + local pidfile=${1:?} + local timeout=${2:-5} + + while [ ! -s "$pidfile" ] ;do + sleep 1 + + # if we reach the timeout, return failure + if [ "$timeout" -eq 0 ] ;then + return 1 + fi + + timeout=$(($timeout-1)) + done + + return 0 +} + +# +# Kill a process. +# +# First argument (required) is the pid to kill. +# +# Second argument (optional) is a pidfile that should be removed after the +# process is killed. +# +# Third argument (optional) is a timeout (in seconds) to wait for the process +# to die. Default timeout is 60 seconds. +# +# Returns 0 (success) if the process is dead or 1 (failure) if the process is +# still running after attempts to kill have completed. +# +kill_pid() +{ + local pid=${1:?} + local pidfile=$2 + local stop_timeout=${3:-60} + + kill -TERM $pid >/dev/null 2>&1 + sleep 0.1 + + timeout=$stop_timeout + kill -0 $pid >/dev/null 2>&1 + while [ $? -eq 0 ] && [ $timeout -ne 0 ]; do + sleep 1 + ((timeout--)) + kill -0 $pid >/dev/null 2>&1 + done + + if [ $timeout -eq 0 ]; then + echo "Process $pid not terminated gracefully after $stop_timeout seconds" 1>&2 + kill -KILL $pid >/dev/null 2>&1 + sleep 1 + kill -0 $pid >/dev/null 2>&1 + timeout=$stop_timeout + while [ $? -eq 0 ] && [ $timeout -ne 0 ]; do + sleep 1 + ((timeout--)) + kill -0 $pid >/dev/null 2>&1 + done + if [ $? -eq 0 ]; then + echo "Process $pid not killed after SIGKILL" 1>&2 + return 1 + else + echo "Process $pid killed after SIGKILL" 1>&2 + fi + fi + + if [ -n "$pidfile" ]; then + rm -f "$pidfile" + fi + + return 0 +} + +init_restart_file() +{ + local restart_file="${1:?}" + local restart_file_base_dir="$(dirname "$restartfile")" + local user="${USER:-puppet}" + local group="${GROUP:-puppet}" + + if [ ! -e "$restartfile" ]; then + /usr/bin/install --directory --owner=$user --group=$group --mode=755 "$restart_file_base_dir" + if [ $? -ne 0 ]; then + echo "Unable to create or set permissions for restart file at ${restart_file_base_dir}" 1>&2 + return 1 + fi + echo -n "0" > "$restart_file" + if [ $? -ne 0 ]; then + echo "Unable to create restart file at ${restart_file}" 1>&2 + return 1 + fi + chown $user:$group "$restart_file" + if [ $? -ne 0 ]; then + echo "Unable to set permissions for restart file at ${restart_file}" 1>&2 + return 1 + fi + elif [ ! -r "$restart_file" ] || [ ! -w "$restart_file" ]; then + echo "The restart-file at ${restart_file} is not readable and/or writeable." 1>&2 + return 1 + fi + + return 0 +} + +if [ "$0" = "$BASH_SOURCE" ] ;then + COMMAND=${1:?} + export $(systemctl show -p MainPID puppetserver.service) + case $COMMAND in + wait_for_app) + wait_for_app ${MainPID:?} ${START_TIMEOUT:-300} + ;; + *) + ;; + esac +fi diff --git a/packaging/resources/files/wrapper/reload b/packaging/resources/files/wrapper/reload new file mode 100755 index 000000000..046e554df --- /dev/null +++ b/packaging/resources/files/wrapper/reload @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set +e + +restartfile="/opt/puppetlabs/server/data/puppetserver/restartcounter" +reload_timeout="${RELOAD_TIMEOUT:-120}" +timeout="$reload_timeout" +realname="puppetserver" + +if [ -d "/run" ]; then + PIDFILE="/run/puppetlabs/${realname}/${realname}.pid" +else + PIDFILE="/var/run/puppetlabs/${realname}/${realname}.pid" +fi + +if [ ! -e "${INSTALL_DIR}/helper-functions.sh" ]; then + echo "Unable to find ${INSTALL_DIR}/helper-functions.sh script, failing start." 1>&2 + exit 1 +fi + +. "${INSTALL_DIR}/helper-functions.sh" + +init_restart_file "$restartfile" || exit $? + +initial="$(head -n 1 "$restartfile")" +pid="$(pgrep -f "puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main")" +kill -HUP $pid >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Service not running so cannot be reloaded" 1>&2 + exit 1 +fi +sleep 0.1 +cur="$(head -n 1 "$restartfile")" +while [ "$cur" == "$initial" ] ;do + kill -0 $pid >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Process $pid exited before reload had completed" 1>&2 + rm -f "$PIDFILE" + exit 1 + fi + sleep 1 + cur="$(head -n 1 "$restartfile")" + + ((timeout--)) + if [ $timeout -eq 0 ]; then + echo "Reload timed out after $reload_timeout seconds" + exit 1 + fi +done + +exit 0 diff --git a/packaging/resources/files/wrapper/start b/packaging/resources/files/wrapper/start new file mode 100755 index 000000000..8525898de --- /dev/null +++ b/packaging/resources/files/wrapper/start @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +set +e + +pid="$(pgrep -f "puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main")" + +restartfile="/opt/puppetlabs/server/data/puppetserver/restartcounter" +start_timeout="${START_TIMEOUT:-300}" + +real_name="puppetserver" + +if [ -d "/run" ]; then + rundir="/run/puppetlabs/${real_name}" +else + rundir="/var/run/puppetlabs/${real_name}" +fi + +app_logdir=${app_logdir:=/var/log/puppetlabs/${real_name}} +PIDFILE="${rundir}/${real_name}.pid" + +cli_defaults=${INSTALL_DIR}/cli/cli-defaults.sh + +if [ ! -e "${INSTALL_DIR}/helper-functions.sh" ]; then + echo "Unable to find ${INSTALL_DIR}/helper-functions.sh script, failing start." 1>&2 + exit 1 +fi + +/usr/bin/install --directory --owner=$USER --group=$GROUP --mode=755 "$rundir" +if [ $? -ne 0 ]; then + echo "Unable to create/set permissions for rundir: ${rundir}" 1>&2 + exit 1 +fi + +. "${INSTALL_DIR}/helper-functions.sh" + +write_pid_file() { + echo "$pid" > "$PIDFILE" + if [ $? -ne 0 ]; then + echo "Unable to write pid file: ${PIDFILE}" 1>&2 + terminate_java_process + exit 1 + fi +} + +terminate_java_process() { + echo "Startup script was terminated before completion" 1>&2 + kill_pid "$pid" "$PIDFILE" "$SERVICE_STOP_RETRIES" + exit 1 +} + +if [ -n "$pid" ]; then + write_pid_file + exit 0 +fi + +rm -f "$PIDFILE" + +init_restart_file "$restartfile" || exit $? + +CLASSPATH=${INSTALL_DIR}/puppet-server-release.jar + +if [ -e "$cli_defaults" ]; then + . $cli_defaults + if [ $? -ne 0 ]; then + echo "Unable to initialize cli defaults, failing start." 1>&2 + exit 1 + fi +fi + +java_version=$($JAVA_BIN -version 2>&1 | head -1 | awk -F\" '{ print $2 }') +java_major_version=$(echo $java_version | awk -F. '{ print $1 }') + +LOG_APPENDER="-Dlogappender=F1" + +out_of_memory_flag='-XX:OnOutOfMemoryError=kill -9 %p' +if [ "$java_major_version" -ge 11 ]; then + out_of_memory_flag="-XX:+CrashOnOutOfMemoryError" +fi + +"$JAVA_BIN" $JAVA_ARGS $LOG_APPENDER \ + "$out_of_memory_flag" \ + -XX:ErrorFile="$app_logdir/${real_name}_err_pid%p.log" \ + -cp "$CLASSPATH" \ + clojure.main \ + -m puppetlabs.trapperkeeper.main \ + --config "$CONFIG" \ + --bootstrap-config "$BOOTSTRAP_CONFIG" \ + --restart-file "$restartfile" \ + $TK_ARGS & + +# $! is the process id of the last backgrounded process, the Java process above. +pid=$! +trap terminate_java_process SIGHUP SIGINT SIGTERM +write_pid_file + +cur="$(head -n 1 "$restartfile")" +initial="$cur" + +timeout="$start_timeout" +while [ "$cur" == "$initial" ] ;do + kill -0 $pid >/dev/null 2>&1 + if [ $? -ne 0 ]; then + rm -f "$PIDFILE" + echo "Background process $pid exited before start had completed" 1>&2 + exit 1 + fi + + sleep 1 + cur="$(head -n 1 "$restartfile")" + + ((timeout--)) + if [ $timeout -eq 0 ]; then + echo "Startup timed out after $start_timeout seconds" 1>&2 + terminate_java_process + rm -f "$PIDFILE" + exit 1 + fi +done + +write_pid_file +exit 0 diff --git a/packaging/resources/files/wrapper/stop b/packaging/resources/files/wrapper/stop new file mode 100755 index 000000000..c3970e4ac --- /dev/null +++ b/packaging/resources/files/wrapper/stop @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set +e + +pid="$(pgrep -f "puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main")" +realname="puppetserver" + +if [ -d "/run" ]; then + PIDFILE="/run/puppetlabs/${realname}/${realname}.pid" +else + PIDFILE="/var/run/puppetlabs/${realname}/${realname}.pid" +fi + +if [ ! -e "${INSTALL_DIR}/helper-functions.sh" ]; then + echo "Unable to find ${INSTALL_DIR}/helper-functions.sh script, failing stop." 1>&2 + exit 1 +fi + +. "${INSTALL_DIR}/helper-functions.sh" + +if [ -z "$pid" ]; then + rm -f "$PIDFILE" + exit 0 +else + kill_pid "$pid" "$PIDFILE" "$SERVICE_STOP_RETRIES" + exit $? +fi diff --git a/packaging/resources/systemd/puppetserver-direct.service.erb b/packaging/resources/systemd/puppetserver-direct.service.erb new file mode 100644 index 000000000..2219fd328 --- /dev/null +++ b/packaging/resources/systemd/puppetserver-direct.service.erb @@ -0,0 +1,62 @@ +# +# Local settings can be configured without being overwritten by package upgrades, for example +# if you want to increase puppetserver open-files-limit to 10000, +# you need to increase systemd's LimitNOFILE setting, so create a file named +# "/etc/systemd/system/puppetserver.service.d/limits.conf" containing: +# [Service] +# LimitNOFILE=10000 +# You can confirm it worked by running systemctl daemon-reload +# then running systemctl show puppetserver | grep LimitNOFILE +# +[Unit] +Description=puppetserver Service +After=syslog.target network.target nss-lookup.target + +[Service] +Type=<%= @settings[:systemd_notify_reload] ? 'notify-reload' : 'notify' %> +LogsDirectory=puppetlabs/puppetserver +RuntimeDirectory=puppetlabs/puppetserver +EnvironmentFile=<%= @platform.defaultdir %>/puppetserver +Environment="JAVA_ARGS_DIST=<%= @settings[:java_args_dist] %>" +User=puppet +TimeoutStartSec=300 +TimeoutStopSec=60 +Restart=on-failure +StartLimitBurst=5 +PrivateTmp=true + +# https://tickets.puppetlabs.com/browse/EZ-129 +# Prior to systemd v228, TasksMax was unset by default, and unlimited. Starting in 228 a default of '512' +# was implemented. This is low enough to cause problems for certain applications. In systemd 231, the +# default was changed to be 15% of the default kernel limit. This explicitly sets TasksMax to 4915, +# which should match the default in systemd 231 and later. +# See https://github.com/systemd/systemd/issues/3211#issuecomment-233676333 +TasksMax=4915 + +#set default privileges to -rw-r----- +UMask=027 + +# The classpath starts with the jars directory so that extra jars, most +# notably the BouncyCastle FIPS provider jars on redhatfips platforms, are +# available to the service. +ExecStart=<%= @settings[:java_bin] %> $JAVA_ARGS_DIST $JAVA_ARGS $LOG_APPENDER -Dlogappender=F1 \ + -XX:+CrashOnOutOfMemoryError \ + -XX:ErrorFile="${LOGS_DIRECTORY}/puppetserver_err_pid%%p.log" \ + -cp "/opt/puppetlabs/server/data/puppetserver/jars/*:${INSTALL_DIR}/puppet-server-release.jar" \ + clojure.main \ + -m puppetlabs.trapperkeeper.main \ + --config "${CONFIG}" \ + --bootstrap-config "${BOOTSTRAP_CONFIG}" \ + --restart-file /run/puppetlabs/puppetserver/restartcounter \ + $TK_ARGS +<% unless @settings[:systemd_notify_reload] -%> + +ExecReload=/opt/puppetlabs/server/apps/puppetserver/bin/puppetserver reload +<% end -%> + +KillMode=process + +SuccessExitStatus=143 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/resources/systemd/puppetserver-wrapper.service.erb b/packaging/resources/systemd/puppetserver-wrapper.service.erb new file mode 100644 index 000000000..ee8e70e71 --- /dev/null +++ b/packaging/resources/systemd/puppetserver-wrapper.service.erb @@ -0,0 +1,46 @@ +# +# Local settings can be configured without being overwritten by package upgrades, for example +# if you want to increase puppetserver open-files-limit to 10000, +# you need to increase systemd's LimitNOFILE setting, so create a file named +# "/etc/systemd/system/puppetserver.service.d/limits.conf" containing: +# [Service] +# LimitNOFILE=10000 +# You can confirm it worked by running systemctl daemon-reload +# then running systemctl show puppetserver | grep LimitNOFILE +# +[Unit] +Description=puppetserver Service +After=syslog.target network.target nss-lookup.target + +[Service] +Type=forking +EnvironmentFile=<%= @platform.defaultdir %>/puppetserver +User=puppet +TimeoutStartSec=300 +TimeoutStopSec=60 +Restart=on-failure +StartLimitBurst=5 +PIDFile=/run/puppetlabs/puppetserver/puppetserver.pid +PrivateTmp=true + +# https://tickets.puppetlabs.com/browse/EZ-129 +# Prior to systemd v228, TasksMax was unset by default, and unlimited. Starting in 228 a default of '512' +# was implemented. This is low enough to cause problems for certain applications. In systemd 231, the +# default was changed to be 15% of the default kernel limit. This explicitly sets TasksMax to 4915, +# which should match the default in systemd 231 and later. +# See https://github.com/systemd/systemd/issues/3211#issuecomment-233676333 +TasksMax=4915 + +#set default privileges to -rw-r----- +UMask=027 + +ExecReload=/opt/puppetlabs/server/apps/puppetserver/bin/puppetserver reload +ExecStart=/opt/puppetlabs/server/apps/puppetserver/bin/puppetserver start +ExecStop=/opt/puppetlabs/server/apps/puppetserver/bin/puppetserver stop + +KillMode=process + +SuccessExitStatus=143 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/resources/systemd/puppetserver.sysconfig.erb b/packaging/resources/systemd/puppetserver.sysconfig.erb new file mode 100644 index 000000000..ffc07ec1a --- /dev/null +++ b/packaging/resources/systemd/puppetserver.sysconfig.erb @@ -0,0 +1,39 @@ +########################################### +# Init settings for puppetserver +########################################### + +# used by openvox-server CLI apps, not by the systemd unit +JAVA_BIN="<%= @settings[:java_bin] %>" + +# Modify this if you'd like to change the memory allocation, enable JMX, etc +JAVA_ARGS="<%= @settings[:java_args] %>" + +# Modify this as you would JAVA_ARGS but for non-service related subcommands +JAVA_ARGS_CLI="${JAVA_ARGS_CLI:-}" + +# Modify this if you'd like TrapperKeeper specific arguments +TK_ARGS="" + +# These normally shouldn't need to be edited if using OS packages +USER="puppet" +GROUP="puppet" +INSTALL_DIR="/opt/puppetlabs/server/apps/puppetserver" +CONFIG="/etc/puppetlabs/puppetserver/conf.d" + +# Bootstrap path +BOOTSTRAP_CONFIG="/etc/puppetlabs/puppetserver/services.d/,/opt/puppetlabs/server/apps/puppetserver/config/services.d/" + +# SERVICE_STOP_RETRIES can be set here to alter the default stop timeout in +# seconds. For systemd, the shorter of this setting or 'TimeoutStopSec' in +# the systemd.service definition will effectively be the timeout which is used. +SERVICE_STOP_RETRIES=60 + +# START_TIMEOUT can be set here to alter the default startup timeout in +# seconds. For systemd, the shorter of this setting or 'TimeoutStartSec' +# in the service's systemd.service configuration file will effectively be the +# timeout which is used. +START_TIMEOUT=300 + +# Maximum number of seconds that can expire for a service reload attempt before +# the result of the attempt is interpreted as a failure. +RELOAD_TIMEOUT=120 diff --git a/packaging/resources/tmpfiles.d/puppetserver.conf b/packaging/resources/tmpfiles.d/puppetserver.conf new file mode 100644 index 000000000..27cae2c39 --- /dev/null +++ b/packaging/resources/tmpfiles.d/puppetserver.conf @@ -0,0 +1 @@ +d /run/puppetlabs/puppetserver 0755 puppet puppet - diff --git a/project.clj b/project.clj index de9f6d723..c06ad96d9 100644 --- a/project.clj +++ b/project.clj @@ -260,6 +260,16 @@ :plugins [[org.openvoxproject/lein-ezbake ~(or (System/getenv "EZBAKE_VERSION") "4.1.0")]] :name "puppetserver"} + ;; Profiles for building the release uberjar without ezbake. Used by + ;; tasks/uberjar.rake, which feeds the vanagon packaging under packaging/. + ;; bcpkix-jdk18on is bundled in the regular jar and is also needed at + ;; packaging time to install vendored gems. The FIPS jar excludes all + ;; BouncyCastle classes and the package installs the separate BC FIPS + ;; jars for the runtime instead. + :pkg {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]} + :pkg-fips {:dependencies [[org.bouncycastle/bcpkix-jdk18on]] + :uberjar-exclusions [#"^org/bouncycastle/.*"]} + :ezbake-fips {:dependencies ^:replace [[org.clojure/clojure] ;; The non-FIPS BC jar is only needed for installing vendored gems ;; at packaging time, and is not included in the final package. diff --git a/tasks/build.rake b/tasks/build.rake index 87a568014..912d8aad5 100644 --- a/tasks/build.rake +++ b/tasks/build.rake @@ -1,5 +1,6 @@ require 'fileutils' require 'tmpdir' +require_relative '../packaging/lib/server_packaging' @image = 'ezbake-builder' @container = 'openvox-server-builder' @@ -23,29 +24,7 @@ rpm_fips, rpm_nonfips = rpm_platforms.split(',').partition { |p| p.start_with?(' @nonfips_rpms = rpm_nonfips.map{ |p| "pl-#{p}-x86_64" }.join(' ') @fips_rpms = rpm_fips.map{ |p| "pl-#{p}-x86_64" }.join(' ') -# The deps must be built in this order due to dependencies between them. -# There is a circular dependency between clj-http-client and trapperkeeper-webserver, -# but only for tests, so the build *should* work. -DEP_BUILD_ORDER = [ - 'clj-kitchensink', - 'clj-i18n', - 'comidi', - 'jvm-ssl-utils', - 'clj-typesafe-config', - 'jruby-deps', - 'trapperkeeper', - 'trapperkeeper-filesystem-watcher', - 'clj-http-client', - 'trapperkeeper-webserver', - 'ring-middleware', - 'jruby-utils', - 'clj-shell-utils', - 'trapperkeeper-authorization', - 'trapperkeeper-metrics', - 'trapperkeeper-scheduler', - 'trapperkeeper-status', - 'trapperkeeper-comidi-metrics', -].freeze +OUTPUT_DIR = File.expand_path('../packaging/output', __dir__) def image_exists !`${DOCKER_BIN-docker} images -q #{@image} --format='{{json .ID}}'`.strip.empty? @@ -71,9 +50,52 @@ def run(cmd) run_command("${DOCKER_BIN-docker} exec #{@container} /bin/bash --login -c '#{cmd}'", silent: false, print_command: true) end +# Mirrors vanagon's Project::DSL#version_from_git so the uberjar archive name +# computed here matches the one the vanagon projects compute for the same ref +def server_package_version + return ENV['OPENVOX_SERVER_VERSION'] unless ENV['OPENVOX_SERVER_VERSION'].to_s.empty? + + describe = run_command('git describe --tags --abbrev=9') + case describe + when /\A\d+\.\d+\.\d+-(?:alpha|beta|rc)\d+\z/ + describe.sub('-', '~') + else + describe.split('-').reject(&:empty?).join('.') + end +end + +# Runs the vanagon build for one project and platform. Outside CI a build of +# openvox-server first builds the uberjar archive it needs when that is +# missing from packaging/output, then points vanagon at that directory. In CI +# the uberjar job has already uploaded the archive, so the openvox-server +# project falls back to the artifacts bucket URL for this version. +def vanagon_build(project, build_platform) + abort "Unexpected project name #{project}" unless project.match?(/\A[a-z0-9-]+\z/) + abort "Unexpected platform #{build_platform}" unless build_platform.match?(/\A[a-z0-9._-]+\z/) + + ENV['SOURCE_DATE_EPOCH'] ||= run_command('git log -1 --format=%ct') + + if project == 'openvox-server' && ENV['SERVER_TARBALL_BASE'].to_s.empty? && ENV['GITHUB_ACTIONS'] != 'true' + variant = ServerPackaging.uberjar_variant(build_platform) + archive = File.join(OUTPUT_DIR, ServerPackaging.uberjar_archive_name(server_package_version, variant)) + vanagon_build('openvox-server-uberjar', ServerPackaging::UBERJAR_PLATFORMS.fetch(variant)) unless File.exist?(archive) + ENV['SERVER_TARBALL_BASE'] = "file://#{OUTPUT_DIR}" + end + + Dir.chdir(File.expand_path('../packaging', __dir__)) do + run_command("bundle exec vanagon build #{project} #{build_platform} --engine docker", + silent: false, print_command: true, report_status: true) + end +end + namespace :vox do - desc 'Build openvox-server packages with Docker' - task :build, [:tag] do |_, args| + desc 'Build openvox-server packages. Project and platform args run the vanagon build, a single ref arg runs the transitional ezbake build.' + task :build, [:tag, :platform] do |_, args| + if args[:platform] && !args[:platform].to_s.empty? + vanagon_build(args[:tag] || 'openvox-server', args[:platform]) + next + end + begin #abort 'You must provide a tag.' if args[:tag].nil? || args[:tag].empty? if args[:tag].nil? || args[:tag].empty? @@ -98,24 +120,7 @@ namespace :vox do } end - deps_to_build = [] - dep_branch = nil - - full_rebuild_branch = ENV['FULL_DEP_REBUILD_BRANCH'] - subset_list = (ENV['DEP_REBUILD'] || '').split(',').map(&:strip).reject(&:empty?) - subset_branch = ENV.fetch('DEP_REBUILD_BRANCH', 'main').to_s - rebuild_org = ENV.fetch('DEP_REBUILD_ORG', 'openvoxproject').to_s - - if full_rebuild_branch && !full_rebuild_branch.strip.empty? - dep_branch = full_rebuild_branch.strip - deps_to_build = DEP_BUILD_ORDER.dup - elsif !subset_list.empty? - dep_branch = subset_branch - unknown = subset_list.reject { |lib| DEP_BUILD_ORDER.include?(lib) } - puts "WARNING: Unknown deps in DEP_REBUILD (will be ignored): #{unknown.join(', ')}" unless unknown.empty? - deps_to_build = DEP_BUILD_ORDER.select { |lib| subset_list.include?(lib) } - end - + deps_to_build, dep_branch, rebuild_org = ServerPackaging.clojure_dep_rebuild deps_to_build.each do |lib| libs_to_build_manually[lib] = { :repo => "https://github.com/#{rebuild_org}/#{lib}", diff --git a/tasks/upload.rake b/tasks/upload.rake index 784d4255e..d38f625bf 100644 --- a/tasks/upload.rake +++ b/tasks/upload.rake @@ -1,59 +1,91 @@ -namespace :vox do - desc 'Upload artifacts from the output directory to S3. Requires the AWS CLI to be installed and configured appropriately.' - task :upload, [:platform] do |_, args| - endpoint = ENV.fetch('ENDPOINT_URL') - bucket = ENV.fetch('BUCKET_NAME') - component = 'openvox-server' - os = nil - arch = nil - if args[:platform] - parts = args[:platform].split('-') - os = parts[0].gsub('fedora','fc') + parts[1] - arch = parts[2] +require_relative '../packaging/lib/server_packaging' + +def s3_command_prefix + endpoint = ENV.fetch('ENDPOINT_URL', nil) + bucket = ENV.fetch('BUCKET_NAME', nil) + abort 'You must set the ENDPOINT_URL environment variable to the S3 server you want to upload to.' if endpoint.nil? || endpoint.empty? + abort 'You must set the BUCKET_NAME environment variable to the S3 bucket you are uploading to.' if bucket.nil? || bucket.empty? + + s3 = "aws s3 --endpoint-url=#{endpoint}" + # Ensure the AWS CLI isn't going to fail with the given parameters + run_command("#{s3} ls s3://#{bucket}/") + [s3, bucket] +end + +def s3_upload_files(files, tag) + s3, bucket = s3_command_prefix + path = "s3://#{bucket}/openvox-server/#{tag}" + files.each do |f| + run_command("#{s3} cp #{f} #{path}/#{File.basename(f)} --no-progress", silent: false) + end +end + +# Uploads the vanagon build output for one platform, or everything in +# packaging/output when no platform is given. Packages carry the short +# platform tag in their names (el9, ubuntu24.04), the uberjar archives carry +# the full vanagon platform name (el-9-x86_64), and the source tarball one +# platform's build emits carries neither. +def vanagon_upload(tag, platform) + munged_tag = tag.gsub('-', '.') + glob = "#{__dir__}/../packaging/output/**/*#{munged_tag}*" + puts "Searching for files with glob #{glob}" + files = Dir.glob(glob).select { |f| File.file?(f) } + + if platform && !platform.to_s.empty? + parts = platform.split('-') + os = parts[0].gsub('fedora', 'fc') + parts[1] + source_tarball = ServerPackaging.source_tarball_name(tag) + files = files.select do |f| + name = File.basename(f) + name.include?(os) || name.include?(platform) || name == source_tarball end + end + abort 'No files for the given tag found in the output directory.' if files.empty? - abort 'You must set the ENDPOINT_URL environment variable to the S3 server you want to upload to.' if endpoint.nil? || endpoint.empty? - abort 'You must set the BUCKET_NAME environment variable to the S3 bucket you are uploading to.' if bucket.nil? || bucket.empty? + s3_upload_files(files, tag) +end - s3 = "aws s3 --endpoint-url=#{endpoint}" +# Transitional upload for the ezbake build output, removed once the vanagon +# pipeline is the only one +def ezbake_upload + s3, bucket = s3_command_prefix - # Ensure the AWS CLI isn't going to fail with the given parameters - run_command("#{s3} ls s3://#{bucket}/") + config = File.expand_path('../target/staging/ezbake.rb', __dir__) + abort "Could not find ezbake config from the build at #{config}" unless File.exist?(config) + load config + version = EZBake::Config.fetch(:version) + release = EZBake::Config.fetch(:release) + # If release is a digit, then we built a tagged version. Otherwise, + # we built a snapshot and want to include that in the path to upload to. + tag = release =~ /^\d{1,2}$/ ? version : "#{version}-#{release}" - config = File.expand_path("../target/staging/ezbake.rb", __dir__) - abort "Could not find ezbake config from the build at #{config}" unless File.exist?(config) - load config - version = EZBake::Config.fetch(:version) - release = EZBake::Config.fetch(:release) - # If release is a digit, then we built a tagged version. Otherwise, - # we built a snapshot and want to include that in the path to upload to. - tag = release =~ /^\d{1,2}$/ ? version : "#{version}-#{release}" + files = Dir.glob("#{__dir__}/../output/**/*#{tag}*") + # Tarballs use a different version format than RPM/DEB packages. + # ezbake generates the tarball name from the git tag (e.g. 8.13.0.SNAPSHOT.2026...) + # while RPM/DEB use the version-release format (e.g. 8.13.0-0.1SNAPSHOT.2026...). + # Pre-releases add a tilde suffix to the version here (e.g. 9.0.0~beta1) that the + # git-tag tarball name lacks, so match on the base version to cover snapshots and + # pre-releases alike. + base_version = version.split('~').first + tarball_files = Dir.glob("#{__dir__}/../output/openvox-server-#{base_version}*.tar.gz") + files = (files + tarball_files).uniq - glob = "#{__dir__}/../output/**/*#{tag}*" - if os - # "arch" is not used here because it's all noarch - glob += "#{os}*" - end - files = Dir.glob(glob) - - # Tarballs use a different version format than RPM/DEB packages. - # ezbake generates the tarball name from the git tag (e.g. 8.13.0.SNAPSHOT.2026...) - # while RPM/DEB use the version-release format (e.g. 8.13.0-0.1SNAPSHOT.2026...). - # Pre-releases add a tilde suffix to the version here (e.g. 9.0.0~beta1) that the - # git-tag tarball name lacks, so match on the base version to cover snapshots and - # pre-releases alike. - unless os - base_version = version.split('~').first - tarball_files = Dir.glob("#{__dir__}/../output/openvox-server-#{base_version}*.tar.gz") - files = (files + tarball_files).uniq - end + abort 'No files for the given tag found in the output directory.' if files.empty? - abort 'No files for the given tag found in the output directory.' if files.empty? + path = "s3://#{bucket}/openvox-server/#{tag}" + files.each do |f| + run_command("#{s3} cp #{f} #{path}/#{File.basename(f)} --no-progress", silent: false) + end +end - path = "s3://#{bucket}/#{component}/#{tag}" - files.each do |f| - run_command("#{s3} cp #{f} #{path}/#{File.basename(f)} --no-progress", silent: false) +namespace :vox do + desc 'Upload built artifacts to S3. With tag and platform args this uploads the vanagon output, with no args it uploads the transitional ezbake output. Requires the AWS CLI to be installed and configured appropriately.' + task :upload, [:tag, :platform] do |_, args| + if args[:tag] && !args[:tag].to_s.empty? + vanagon_upload(args[:tag], args[:platform]) + else + ezbake_upload end end end