Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ profile: production
exclude_paths:
- .cache/
- .github/
- extensions/molecule/

warn_list:
- experimental
130 changes: 130 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---

name: Bug report
description: Something went wrong during a rotation
labels: [bug]
body:
- type: markdown
attributes:
value: >-
Before filing, please check the [troubleshooting
section](https://github.com/krameff/ssh_key_rotation#troubleshooting) of
the README. It covers the common failure modes, including crypto-policy
rejections and `sshd_config.d` drop-ins overriding what the playbook
writes.


If you have found a way this collection can lock a host out, please
report it privately instead. See the security link on the previous page.

- type: textarea
id: what-happened
attributes:
label: What happened
description: What did you expect, and what did you get instead?
validations:
required: true

- type: dropdown
id: phase
attributes:
label: Which phase failed
options:
- "Phase 0: validate, on the control node"
- "Phase 1: install, connected with the old key"
- "Phase 2: verify, reconnecting with the new key"
- Not sure
validations:
required: true

- type: dropdown
id: host-state
attributes:
label: Was the host left reachable
description: >-
This is the most important field. If a run left a host unreachable, that
is a safety model failure and will be prioritised over everything else.
options:
- "Yes, the old key still works"
- "Yes, the new key works"
- "No, the host is unreachable"
- Not sure yet
validations:
required: true

- type: textarea
id: command
attributes:
label: The command you ran
description: Redact key paths if they are sensitive.
render: shell
validations:
required: true

- type: textarea
id: output
attributes:
label: Failing task and error output
description: >-
The failing task name and its error. Output from a run with `-vvv` is
much more useful. Please redact any key material.
render: text
validations:
required: true

- type: input
id: target-os
attributes:
label: Target OS and version
placeholder: "AlmaLinux 9.8, Ubuntu 22.04, openSUSE Leap 15.6"
validations:
required: true

- type: input
id: ansible-version
attributes:
label: Ansible version
description: Output of `ansible --version` on the control node.
placeholder: "ansible [core 2.18.1]"
validations:
required: true

- type: input
id: collection-version
attributes:
label: Collection version
placeholder: "0.9.0, or a commit SHA if installed from source"
validations:
required: true

- type: input
id: key-types
attributes:
label: Old and new key types
placeholder: "old rsa 4096, new ed25519"

- type: input
id: crypto-policy
attributes:
label: Crypto-policy on the target, if RHEL or Fedora family
description: Output of `update-crypto-policies --show`.
placeholder: "FIPS, DEFAULT:PQ, or leave blank if not applicable"

- type: textarea
id: dropins
attributes:
label: sshd_config drop-in files on the target
description: >-
Output of `ls /etc/ssh/sshd_config.d/`. These frequently override what
the playbook writes.
render: text

- type: checkboxes
id: confirmations
attributes:
label: Before submitting
options:
- label: I have read the troubleshooting section of the README
required: true
- label: I have redacted all private key material from this report
required: true
17 changes: 17 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---

blank_issues_enabled: false

contact_links:
- name: Lockout or other security issue
url: https://github.com/krameff/ssh_key_rotation/security/advisories/new
about: >-
If you have found a way this collection can lock a host out, or any other
security issue, please report it privately rather than opening a public
issue. You can also email security@krameff.com.

- name: Question about usage
url: https://github.com/krameff/ssh_key_rotation/discussions
about: >-
For "how do I" questions, start a discussion. Check the README
troubleshooting section first, it covers the common failure modes.
51 changes: 51 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---

name: Feature request
description: Suggest a capability or change
labels: [enhancement]
body:
- type: markdown
attributes:
value: >-
Please check the "Future enhancements" section of
[DEVELOPMENT.md](https://github.com/krameff/ssh_key_rotation/blob/main/DEVELOPMENT.md)
first, in case it is already on the list.

- type: textarea
id: problem
attributes:
label: What problem would this solve
description: >-
Describe the situation you are actually in, not just the feature you
have in mind. It often turns out there is a better fit than the one
first thought of.
validations:
required: true

- type: textarea
id: proposal
attributes:
label: What you would like it to do
validations:
required: true

- type: textarea
id: safety
attributes:
label: Effect on the safety model
description: >-
Would this change when the old key is removed, when `sshd_config` is
validated or backed up, or how the verify stage's rollback works? See
the [safety
model](https://github.com/krameff/ssh_key_rotation#safety-model). Say so
plainly if it would; that is not automatically a blocker, but it needs
discussing up front.
placeholder: "No effect, this is additive and off by default."
validations:
required: true

- type: textarea
id: alternatives
attributes:
label: Alternatives you have considered
description: Including whether an existing variable already gets you close.
51 changes: 51 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!--
Pull requests are accepted from existing contributors only. See CONTRIBUTING.md.
If you are not yet a contributor, please open an issue instead, or email github@krameff.com.
-->

## What this changes

<!-- What does it do, and why is it needed? Link the issue if there is one. -->

## Effect on the safety model

<!--
This collection rotates SSH keys on live hosts. A bug here does not produce a wrong
answer, it produces a machine nobody can log into.

If this change touches any of the following, say so here and explain why it is still
safe. If it touches none of them, say "no effect".

- When the old key is removed relative to proving the new one
- `sshd -t` validation before an sshd_config write
- Backups of sshd_config or authorized_keys
- Reload rather than restart
- The block/rescue in verify.yml, or the connection reset that precedes it
-->

## How this was tested

<!--
Which scenarios were run, and against what. If you tested against a real host, say
which OS and which crypto-policy.

If a new assertion was added, confirm you broke the thing it checks and watched it
go red. A test that cannot fail is worse than no test.
-->

## Checklist

- [ ] `ansible-lint` passes with no new warnings
- [ ] `ansible-playbook playbooks/rotate.yml --syntax-check` passes
- [ ] `molecule test -s default` passes
- [ ] `molecule test -s rollback` passes
- [ ] New behaviour is covered by a test that fails without the change
- [ ] The safety model is preserved, or the change to it is called out above
- [ ] README.md updated for user-facing changes
- [ ] CHANGELOG.md updated under `## [Unreleased]`
- [ ] `galaxy.yml` version is unchanged; releases are cut separately by tag
- [ ] No new dependencies without justification
- [ ] Backwards compatible, or the breaking change is documented
- [ ] No key material, private or public, committed

<!-- If a box is unchecked, say why rather than removing it. -->
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---

name: CI

on:
Expand All @@ -12,10 +13,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: "3.12"

Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/molecule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---

name: Molecule

on:
push:
branches: [main, devel]
pull_request:
branches: [main, devel]
# The test platforms track the geerlingguy images by :latest, so the base images move
# underneath this suite between commits. A weekly run means that drift surfaces as its own
# red build rather than as a mystery failure on whatever unrelated PR is open at the time.
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

jobs:
molecule:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# nonroot is not a variant of default for its own sake: default and rollback both enter
# the containers as root and rotate root, so a file left owned by root is the correct
# outcome there and an ownership regression cannot fail them. nonroot is the only
# scenario that can catch it.
scenario: [default, rollback, nonroot]
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.12"

- name: Install Ansible, Molecule, and the docker driver
run: |
python -m pip install --upgrade pip
pip install "ansible>=2.18" "molecule>=25.0" "molecule-plugins[docker]"

# No separate ansible-galaxy step: the scenarios run `dependency` as the first step of
# their test sequence, so collection dependencies get installed the same way here and on
# a developer's machine.
- name: Run Molecule scenario
run: molecule test -s ${{ matrix.scenario }}
env:
PY_COLORS: "1"
ANSIBLE_FORCE_COLOR: "1"
Loading