diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 40343da..871c1b1 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.0.1 +current_version = 0.1.0 commit = True message = Bumps version to {new_version} tag = False diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58dd810..7c91ec4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,3 +9,19 @@ concurrency: jobs: test: uses: plus3it/actions-workflows/.github/workflows/test.yml@ddd67e99878a285f728de398c1116151c2d7791a + + linux: + uses: plus3it/actions-workflows/.github/workflows/test-salt-linux.yml@ddd67e99878a285f728de398c1116151c2d7791a + strategy: + matrix: + os_version: + - 8 + - 9 + salt_state: + - postman-api + salt_pillar_root: + - ./tests/pillar/test-linux-main + with: + salt-os-version: ${{ matrix.os_version }} + salt-state: ${{ matrix.salt_state }} + salt-pillar-root: ${{ matrix.salt_pillar_root }} diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b42ed..c2b139a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +### 0.1.0 + +**Released**: 2026.06.02 + +**Summary**: + +* Added ("Enterprise") Linux functionality + * Installs the Postman API binary (as downloaded from [vendor site](https://www.postman.com/downloads/)) + * Install-location defaults to `/opt/postman` + * Install-location overrideable via Pillar's `install_root` parameter + * For RHEL 9 (and related distros), latest installable version is 10.24.26 (override via Pillar's `download_uri` parameter) + * Creates a wrapper-script at `/usr/local/bin/postman` to ensure appropriate launch-time arguments. For example: + * "don't try to use GPU on X-over-SSH tunnels" + * "disable sandboxing on STIGed operating systems" ( override via Pillar's `sandbox_enabled` parameter) + * "require use of TLS v1.2+" ( override via Pillar's `ssl_min_version` parameter) + * Sets appropriate file-modes and SELinux contexts on binaries and wrappers + * Implements "cleanup" for all of the preceeding +* Adds pillar.example to explain parameters/inputs that may be specified via Pillar +* Update README with platform-notes + + ### 0.0.1 -**Released**: 2026.05.22 +**Released**: 2026.06.01 **Summary**: diff --git a/README.md b/README.md index a791bf9..477a56a 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,11 @@ Executes _just_ the `config` state to uninstall the Postman API client-configura ## Compatibility Notes: +### Linux + +1. Due to library compatibilities, the installable version of Postman on RHEL 9 (and derivatives) is constrained to < `11.x`. This formula defaults the RHEL 9 (and derivatives) installation to Postman version `10.24.26` +1. To support hardened enterprise baselines (such as the DISA STIG or CIS profiles), this formula defaults to disabling the Chromium application sandbox (`sandbox_enabled: false`) on Red Hat family distributions. These security profiles typically disable unprivileged user namespaces (`user.max_user_namespaces = 0`), which causes Electron-based applications to crash instantly on startup. For less restrictive environments where user namespaces are permitted, the sandbox can be safely re-enabled by setting `sandbox_enabled: true` via Pillar data. + [^1]: As of this README's writing, only Enterprise Linux and related distros (Red Hat and Oracle Enterprise, CentOS Stream, Rocky and Alma Linux). It has only been specifically tested with EL **_9_** variants. [^2]: As of this README's writing, this functionality has only been tested on Windows Server 2022 diff --git a/README_backrevs.md b/README_backrevs.md new file mode 100644 index 0000000..7ea0481 --- /dev/null +++ b/README_backrevs.md @@ -0,0 +1,35 @@ +# How to Find/Install back-rev versions + +It is primarily expected that this formula will be used to install the "latest and greatest" version of the Postman API application from the vendor's web-site. If, however, a site requires the ability to install a specific — and almost certainly "back rev" — version of the Postman API application, it will be necessary to step through some hoops to find the desired download URL. + +## Identifying available point-releases + +To grab an exhustive list of available Postman versions, execute: + +```bash +$ curl -sL "https://dl.pstmn.io/changelog?channel=stable&platform=linux" | \ +tr '"' '\n' | \ +grep -oE '^[0-9]+\.[0-9]+\.[0-9]+' | \ +sort -rV | \ +uniq +``` + +As mentioned in the main README file's notes for Linux, RHEL 9 distros require a Postman version less than `11.x` + +## Constructing the download URL + +To fetch an arbitrary Postman version from the Vendor's download-service, you will need to construct an appropriate URL path. The general URL path will look like: + +``` + https://dl.pstmn.io/download/version//' +``` + +* The value of `` is as taken from the list output from the BASH scriptlet in the preceding, "Identifying available point-releases", section. +* The value of `` will be either of + * `linux64` for Linux distributions using the x86_64 CPU-architecture + * `windows` for all Windows versions + +By way of example: + +* The URL `https://dl.pstmn.io/download/version/10.24.26/linux64` would be used to pull Postman `10.24.26` for Linux distros +* The URL `https://dl.pstmn.io/download/version/12.12.5/windows` would be used to pull Postman `12.12.5` for Windows systems diff --git a/pillar.example b/pillar.example new file mode 100644 index 0000000..0e1b85f --- /dev/null +++ b/pillar.example @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +--- +postman-api: + config: + # Path where the system desktop entry file will be generated. + desktop_entry: '/usr/share/applications/postman.desktop' + + # Relative path to the application icon inside the installation directory. + icon_source: '/app/resources/app/assets/icon.png' + + # The root directory tree where the application archive is extracted. + # Natively supports paths containing spaces (e.g., '/opt/Desktop Apps/Postman'). + install_root: '/opt/Postman' + + # Controls Chromium's internal application isolation sandbox layer. + # Set to false to support profiles that disable unprivileged namespaces + # (such as DISA STIG or CIS benchmarks), allowing safe startup. + sandbox_enabled: false + + # The SELinux context type applied to the application root directory. + # Automatically triggers fcontext and restorecon operations if the + # kernel security subsystem is running in Enforcing or Permissive mode. + selinux_fcontext: 'usr_t' + + # Mandates the minimum TLS protocol allowed for transit connections. + # Ensures compliance with FIPS validation rules by preventing downgrades. + ssl_min_version: 'tls1.2' + + # Path to the system binary utility that updates the desktop MIME cache. + update_mime_database: '/usr/bin/update-desktop-database' + + # Controls registration of trusted application paths with fapolicyd. + # Set to true to inject execution rules when whitelisting is enabled. + whitelist_enabled: true + + # The system path for the generated wrapper execution bash script. + wrapper_bin: '/usr/local/bin/postman' + + pkg: + # Optional cryptographic hash string (e.g., sha256) to verify the source. + # Leave empty to skip verification when pulling dynamic latest payloads. + download_sig: '' + + # The fully qualified URI source pointing to the target install archive. + # Can point to the public CDN or an internally hosted mirror repository. + download_uri: 'https://dl.pstmn.io/download/version/10.24.26/linux64' + + # The internal name designation used for formula mapping identification. + name: 'postman-api' +... diff --git a/postman-api/clean.sls b/postman-api/clean.sls index 9e45fb4..0fba65d 100644 --- a/postman-api/clean.sls +++ b/postman-api/clean.sls @@ -2,7 +2,5 @@ # vim: ft=sls include: - - .subcomponent.clean - - .service.clean - .config.clean - .package.clean diff --git a/postman-api/config/lin_clean.sls b/postman-api/config/lin_clean.sls new file mode 100644 index 0000000..cd2dc8f --- /dev/null +++ b/postman-api/config/lin_clean.sls @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import mapdata as postman_api with context %} + +{#- Dynamically check if the host has the SELinux kernel subsystem live #} +{%- set selinux_live = salt['grains.get']('selinux:enabled', False) %} + +Refresh Whitelist Daemon Database: + cmd.run: + - name: 'fapolicyd-cli --update' + - onchanges: + - file: 'Remove Whitelist Daemon Policy' + - onlyif: 'command -v fapolicyd-cli' + +Remove Postman Desktop Shortcut: + file.absent: + - name: '{{ postman_api.config.desktop_entry }}' + +{%- if postman_api.config.get('selinux_fcontext', False) and selinux_live %} +Remove Postman SELinux File Contexts: + selinux.fcontext_policy_absent: + - name: '{{ postman_api.config.install_root | replace(" ", "\s") }}(/.*)?' +{%- endif %} + +Remove Protocol Deep Linking Registration: + cmd.run: + - name: '{{ postman_api.config.update_mime_database }} /usr/share/applications' + - onchanges: + - file: 'Remove Postman Desktop Shortcut' + +Remove Whitelist Daemon Policy: + file.absent: + - name: '/etc/fapolicyd/rules.d/95-postman.rules' + - onlyif: 'command -v fapolicyd-cli' + +Suppress Automatic Updates Globally: + host.absent: + - ip: '127.0.0.1' + - name: 'dl.pstmn.io' diff --git a/postman-api/config/lin_file.sls b/postman-api/config/lin_file.sls new file mode 100644 index 0000000..9d59ae3 --- /dev/null +++ b/postman-api/config/lin_file.sls @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import mapdata as postman_api with context %} +{%- from tplroot ~ "/libtofs.jinja" import files_switch with context %} + +{#- Dynamically check if the host has the SELinux kernel subsystem live #} +{%- set selinux_live = salt['grains.get']('selinux:enabled', False) %} + +Configure Postman Desktop Shortcut: + file.managed: + - context: + postman_api: {{ postman_api | json }} + - group: 'root' + - makedirs: True + - mode: '0644' + - name: '{{ postman_api.config.desktop_entry }}' + - source: +{{ files_switch(['postman.desktop', 'postman.desktop.jinja'], + lookup='desktop_shortcut') }} + - template: 'jinja' + - user: 'root' + +{%- if postman_api.config.get('selinux_fcontext', False) and selinux_live %} +Configure Postman SELinux File Contexts: + selinux.fcontext_policy_present: + - filetype: 'a' + - name: '{{ postman_api.config.install_root | replace(" ", "\s") }}(/.*)?' + - sel_type: {{ postman_api.config.selinux_fcontext }} +{%- endif %} + +{%- if postman_api.config.get('whitelist_enabled', False) %} +{#- Escape whitespaces specifically to satisfy strict fapolicyd syntax rules -#} +{%- set fapolicyd_root = postman_api.config.install_root | replace(' ', '\ ') %} +{%- set fapolicyd_wrap = postman_api.config.wrapper_bin | replace(' ', '\ ') %} +Configure Whitelist Daemon Policy: + file.managed: + - contents: | + # Allow execution of system-wide Postman binaries and libraries + allow perm=any uid=all : dir={{ fapolicyd_root }}/ + allow perm=any uid=all : path={{ fapolicyd_wrap }} + - group: 'root' + - makedirs: True + - mode: '0644' + - name: '/etc/fapolicyd/rules.d/95-postman.rules' + - onlyif: 'command -v fapolicyd-cli' + - user: 'root' +{%- endif %} + +Refresh Whitelist Daemon Database: + cmd.run: + - name: 'fapolicyd-cli --update' + - onchanges: + - file: 'Configure Whitelist Daemon Policy' + - onlyif: 'command -v fapolicyd-cli' + +Register Protocol Deep Linking: + cmd.run: + - name: '{{ postman_api.config.update_mime_database }} /usr/share/applications' + - onchanges: + - file: 'Configure Postman Desktop Shortcut' + +{%- if selinux_live %} +{%- set root_path = postman_api.config.install_root %} +{%- set wrap_path = postman_api.config.wrapper_bin %} +Restore SELinux Security Contexts: + cmd.run: + - name: 'restorecon -R "{{ root_path }}" "{{ wrap_path }}"' + - onchanges: + - file: 'Configure Postman Desktop Shortcut' + {%- if postman_api.config.get('selinux_fcontext', False) %} + - selinux: 'Configure Postman SELinux File Contexts' + {%- endif %} + - onlyif: 'test -d "{{ root_path }}" && test -e "{{ wrap_path }}"' +{%- endif %} + +Suppress Automatic Updates Globally: + host.present: + - ip: '127.0.0.1' + - name: 'dl.pstmn.io' diff --git a/postman-api/config/win_clean.sls b/postman-api/config/win_clean.sls new file mode 100644 index 0000000..e69de29 diff --git a/postman-api/config/win_file.sls b/postman-api/config/win_file.sls new file mode 100644 index 0000000..e69de29 diff --git a/postman-api/files/default/postman.desktop.jinja b/postman-api/files/default/postman.desktop.jinja new file mode 100644 index 0000000..18972a0 --- /dev/null +++ b/postman-api/files/default/postman.desktop.jinja @@ -0,0 +1,9 @@ +[Desktop Entry] +Categories=Development; +Comment=Postman API Platform +Exec={{ postman_api.config.wrapper_bin }} %u +Icon="{{ postman_api.config.install_root }}{{ postman_api.config.icon_source }}" +MimeType=x-scheme-handler/postman; +Name=Postman +Terminal=false +Type=Application diff --git a/postman-api/init.sls b/postman-api/init.sls index 275d1fc..717b68e 100644 --- a/postman-api/init.sls +++ b/postman-api/init.sls @@ -4,5 +4,3 @@ include: - .package - .config - - .service - - .subcomponent diff --git a/postman-api/package/lin_clean.sls b/postman-api/package/lin_clean.sls new file mode 100644 index 0000000..15dbc94 --- /dev/null +++ b/postman-api/package/lin_clean.sls @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import mapdata as postman_api with context %} + +Remove Postman Application Directory: + file.absent: + - name: '{{ postman_api.config.install_root }}' + +Remove Postman Wrapper Script: + file.absent: + - name: '{{ postman_api.config.wrapper_bin }}' diff --git a/postman-api/package/lin_install.sls b/postman-api/package/lin_install.sls new file mode 100644 index 0000000..eac38c0 --- /dev/null +++ b/postman-api/package/lin_install.sls @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import mapdata as postman_api with context %} + +{#- Calculate the parent directory for extraction destination drops #} +{%- set install_dir = postman_api.config.install_root %} +{%- set parent_dir = install_dir.split('/')[:-1] | join('/') %} + +Deploy Postman Wrapper Script: + file.managed: + - contents: | + #!/bin/bash + FLAGS=("--log-level=3") + {%- if not postman_api.config.get('sandbox_enabled', True) %} + FLAGS+=("--no-sandbox") + {%- endif %} + {%- if postman_api.config.get('ssl_min_version', False) %} + FLAGS+=("--ssl-version-min={{ postman_api.config.ssl_min_version }}") + {%- endif %} + # Disable GPU if connected via SSH or an X11 tunnel + if [[ -n "$SSH_CLIENT" ]] || \ + [[ -n "$SSH_TTY" ]] || \ + [[ "$DISPLAY" =~ ^localhost ]] + then + FLAGS+=("--disable-gpu") + fi + exec "{{ install_dir }}/Postman" "${FLAGS[@]}" "$@" 2>/dev/null + - group: 'root' + - mode: '0755' + - name: '{{ postman_api.config.wrapper_bin }}' + - require: + - archive: 'Extract Postman Archive' + - user: 'root' + +Extract Postman Archive: + archive.extracted: + - archive_format: 'tar' + - enforce_toplevel: False + - group: 'root' + - keep_source: False + - name: '{{ parent_dir }}' + - require: + - host: 'Permit Download Domain Access' + - pkg: 'Install Postman Dependencies' + {%- if postman_api.pkg.download_sig %} + - source: '{{ postman_api.pkg.download_uri }}' + - source_hash: '{{ postman_api.pkg.download_sig }}' + {%- else %} + - skip_verify: True + - source: '{{ postman_api.pkg.download_uri }}' + {%- endif %} + - user: root + +Install Postman Dependencies: + pkg.installed: + - pkgs: + - alsa-lib + - at-spi2-atk + - at-spi2-core + - atk + - cairo + - cups-libs + - dbus-glib + - dejavu-sans-fonts + - gdk-pixbuf2 + - gtk3 + - libX11 + - libX11-xcb + - libXScrnSaver + - libXcomposite + - libXcursor + - libXdamage + - libXext + - libXfixes + - libXi + - libXrandr + - libXrender + - libXtst + - libdrm + - libsecret + - libva + - libxcb + - libxkbcommon + - libxshmfence + - mesa-libgbm + - nspr + - nss + - nss-tools + - pango + - vulkan-loader + - xdg-utils + - xorg-x11-xauth + +Permit Download Domain Access: + host.absent: + - ip: '127.0.0.1' + - name: 'dl.pstmn.io' diff --git a/postman-api/package/win_clean.sls b/postman-api/package/win_clean.sls new file mode 100644 index 0000000..e69de29 diff --git a/postman-api/package/win_install.sls b/postman-api/package/win_install.sls new file mode 100644 index 0000000..e69de29 diff --git a/postman-api/parameters/defaults.yaml b/postman-api/parameters/defaults.yaml index 11a6b16..819b999 100644 --- a/postman-api/parameters/defaults.yaml +++ b/postman-api/parameters/defaults.yaml @@ -4,15 +4,19 @@ # Set default values. --- values: + config: + desktop_entry: '' + icon_source: '' + install_root: '' + sandbox_enabled: true + selinux_fcontext: '' + ssl_min_version: '' + update_mime_database: '' + whitelist_enabled: false + wrapper_bin: '' pkg: - name: postman-api - rootgroup: root - config: '/etc/postman-api' - service: - name: postman-api - subcomponent: - config: '/etc/postman-api-subcomponent-formula.conf' - # Just here for testing - added_in_defaults: defaults_value - winner: defaults + download_sig: '' + download_uri: '' + name: 'postman-api' + service: {} ... diff --git a/postman-api/parameters/os_family/RedHat.yaml b/postman-api/parameters/os_family/RedHat.yaml deleted file mode 100644 index e6bc65b..0000000 --- a/postman-api/parameters/os_family/RedHat.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: ft=yaml -# -# Set values specific to: -# salt['config.get']('os_family') == RedHat. -# -# You just need to add the key:values for this `os_family` that differ -# from `defaults.yaml` + `.yaml`. -# -# If you do not need to provide defaults via the `os_family` config, -# you can remove this file or provide at least an empty dict, e.g. -# values: {} ---- -values: - pkg: - name: postman-api-redhat - config: /etc/postman-api.conf -... diff --git a/postman-api/parameters/os_family/RedHat.yaml.jinja b/postman-api/parameters/os_family/RedHat.yaml.jinja new file mode 100644 index 0000000..04fa54a --- /dev/null +++ b/postman-api/parameters/os_family/RedHat.yaml.jinja @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +# +# Set values specific to the RedHat family based on OS major version. +--- +values: + config: + desktop_entry: '/usr/share/applications/postman.desktop' + icon_source: '/app/resources/app/assets/icon.png' + install_root: '/opt/Postman' + sandbox_enabled: false + selinux_fcontext: 'usr_t' + ssl_min_version: 'tls1.2' + update_mime_database: '/usr/bin/update-desktop-database' + whitelist_enabled: true + wrapper_bin: '/usr/local/bin/postman' + pkg: + download_sig: '' + {%- if salt['grains.get']('osmajorrelease') | int == 9 %} + download_uri: 'https://dl.pstmn.io/download/version/10.24.26/linux64' + {%- else %} + download_uri: 'https://dl.pstmn.io/download/latest/linux_64' + {%- endif %} + name: 'postman-api' + service: {} +... diff --git a/tests/pillar/postman-api/main.sls b/tests/pillar/postman-api/main.sls new file mode 100644 index 0000000..68e700c --- /dev/null +++ b/tests/pillar/postman-api/main.sls @@ -0,0 +1,10 @@ +nosql-booster: + lookup: + {%- if grains.os_family == "RedHat" %} + pkg: + download_uri: https://dl.pstmn.io/download/version/10.24.16/linux_64 + config: + sandbox_enabled: true + install_root: '/opt/Desktop Applications/Postman' + {%- elif grains.os_family == "Windows" %} + {%- endif %} diff --git a/tests/pillar/postman-api/top.sls b/tests/pillar/postman-api/top.sls new file mode 100644 index 0000000..3692e36 --- /dev/null +++ b/tests/pillar/postman-api/top.sls @@ -0,0 +1,3 @@ +base: + '*': + - main diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..df96849 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1 @@ +salt-minion