diff --git a/.github/workflows/cli-docs.yml b/.github/workflows/cli-docs.yml new file mode 100644 index 00000000..46121d3d --- /dev/null +++ b/.github/workflows/cli-docs.yml @@ -0,0 +1,44 @@ +name: CLI Docs + +# Runs CI when code merges to the CLI on main +on: + push: + branches: + - main + paths: + - "main.go" + +jobs: + check-updates: + name: Check for updates to the CLI docs + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: 'go.mod' + - name: Generate CLI docs + id: changed-files + run: | + set +e + go run . gendocs + git diff --exit-code --quiet + changedfiles=$? + echo "changedfiles=$changedfiles" >> $GITHUB_OUTPUT + - name: Open pull request for changes + if: steps.changed-files.outputs.changedfiles == '1' + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: cli-docs + commit-message: Update CLI documentation + title: Update CLI documentation + body: | + Auto-generated documentation update for the opkssh CLI commands. + labels: | + docs diff --git a/docs/cli/README.md b/docs/cli/README.md new file mode 100644 index 00000000..ba5affd9 --- /dev/null +++ b/docs/cli/README.md @@ -0,0 +1,11 @@ +# CLI Documentation + +You can start with the [opkssh documentation](opkssh.md). + +## Updates + +Note: The documenation provided here is automatically generated based on the +command line implementation. Please do not modify these files directly. If +there are issues with the documentation, or you would like to add better +documentation, please edit the code in `main.go` and run `go run . gendocs` to +regenerate updated files. diff --git a/docs/cli/opkssh.md b/docs/cli/opkssh.md new file mode 100644 index 00000000..9e3e00f7 --- /dev/null +++ b/docs/cli/opkssh.md @@ -0,0 +1,40 @@ +## opkssh + +SSH with OpenPubkey + +### Synopsis + +SSH with OpenPubkey + +This program allows users to: + - Login and create SSH key pairs using their OpenID Connect identity + - Add policies to auth_id policy files + - Verify OpenPubkey SSH certificates for use with sshd's AuthorizedKeysCommand + +``` +opkssh [flags] +``` + +### Examples + +``` + opkssh login + opkssh add root alice@example.com https://accounts.google.com +``` + +### Options + +``` + -h, --help help for opkssh +``` + +### SEE ALSO + +* [opkssh add](opkssh_add.md) - Appends new rule to the policy file +* [opkssh client](opkssh_client.md) - Interact with client configuration +* [opkssh inspect](opkssh_inspect.md) - Inspect and view details of an opkssh generated SSH key +* [opkssh login](opkssh_login.md) - Authenticate with an OpenID Provider to generate an SSH key for opkssh +* [opkssh readhome](opkssh_readhome.md) - Read the principal's home policy file +* [opkssh verify](opkssh_verify.md) - Verify an SSH key (used by sshd AuthorizedKeysCommand) + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_add.md b/docs/cli/opkssh_add.md new file mode 100644 index 00000000..64f7b40b --- /dev/null +++ b/docs/cli/opkssh_add.md @@ -0,0 +1,39 @@ +## opkssh add + +Appends new rule to the policy file + +### Synopsis + +Add appends a new policy entry in the auth_id policy file granting SSH access to the specified email or subscriber ID (sub) or group. + +It first attempts to write to the system-wide file (/etc/opk/auth_id). If it lacks permissions to update this file it falls back to writing to the user-specific file (~/.opk/auth_id). + +Arguments: + PRINCIPAL The target user account (requested principal). + EMAIL|SUB|GROUP Email address, subscriber ID or group authorized to assume this principal. If using an OIDC group, the argument needs to be in the format of oidc:groups:. + ISSUER OpenID Connect provider (issuer) URL associated with the email/sub/group. + + +``` +opkssh add [flags] +``` + +### Examples + +``` + opkssh add root alice@example.com https://accounts.google.com + opkssh add alice 103030642802723203118 https://accounts.google.com + opkssh add developer oidc:groups:developer https://accounts.google.com +``` + +### Options + +``` + -h, --help help for add +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_client.md b/docs/cli/opkssh_client.md new file mode 100644 index 00000000..8e4dc87e --- /dev/null +++ b/docs/cli/opkssh_client.md @@ -0,0 +1,22 @@ +## opkssh client + +Interact with client configuration + +### Examples + +``` + opkssh client provider list +``` + +### Options + +``` + -h, --help help for client +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey +* [opkssh client provider](opkssh_client_provider.md) - Interact with provider configuration + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_client_provider.md b/docs/cli/opkssh_client_provider.md new file mode 100644 index 00000000..97b74798 --- /dev/null +++ b/docs/cli/opkssh_client_provider.md @@ -0,0 +1,22 @@ +## opkssh client provider + +Interact with provider configuration + +### Examples + +``` + opkssh client provider list +``` + +### Options + +``` + -h, --help help for provider +``` + +### SEE ALSO + +* [opkssh client](opkssh_client.md) - Interact with client configuration +* [opkssh client provider list](opkssh_client_provider_list.md) - List configured providers + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_client_provider_list.md b/docs/cli/opkssh_client_provider_list.md new file mode 100644 index 00000000..1323ff27 --- /dev/null +++ b/docs/cli/opkssh_client_provider_list.md @@ -0,0 +1,26 @@ +## opkssh client provider list + +List configured providers + +``` +opkssh client provider list [flags] +``` + +### Examples + +``` + opkssh client provider list +``` + +### Options + +``` + --config-path string Path to the client config file. Default: ~/.opk/config.yml on linux and %APPDATA%\.opk\config.yml on windows. + -h, --help help for list +``` + +### SEE ALSO + +* [opkssh client provider](opkssh_client_provider.md) - Interact with provider configuration + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_inspect.md b/docs/cli/opkssh_inspect.md new file mode 100644 index 00000000..be9737f8 --- /dev/null +++ b/docs/cli/opkssh_inspect.md @@ -0,0 +1,25 @@ +## opkssh inspect + +Inspect and view details of an opkssh generated SSH key + +``` +opkssh inspect [flags] +``` + +### Examples + +``` + opkssh inspect ~/.ssh/id_ecdsa_sk-cert.pub +``` + +### Options + +``` + -h, --help help for inspect +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_login.md b/docs/cli/opkssh_login.md new file mode 100644 index 00000000..19e9ec65 --- /dev/null +++ b/docs/cli/opkssh_login.md @@ -0,0 +1,49 @@ +## opkssh login + +Authenticate with an OpenID Provider to generate an SSH key for opkssh + +### Synopsis + +Login creates opkssh SSH keys + +Login generates a key pair, then opens a browser to authenticate the user with the OpenID Provider. Upon successful authentication, opkssh creates an SSH public key (~/.ssh/id_ecdsa) containing the user's PK token. By default, this SSH key expires after 24 hours, after which the user must run "opkssh login" again to generate a new key. + +Users can then SSH into servers configured to use opkssh as the AuthorizedKeysCommand. The server verifies the PK token and grants access if the token is valid and the user is authorized per the auth_id policy. +Arguments: + alias The provider alias to use. If not specified, the OPKSSH_DEFAULT provider will be used. The aliases are defined by the OPKSSH_PROVIDERS environment variable. The format is ,,,, + + +``` +opkssh login [alias] [flags] +``` + +### Examples + +``` + opkssh login + opkssh login google + opkssh login --provider=,,, +``` + +### Options + +``` + --auto-refresh Automatically refresh PK token after login + --config-path string Path to the client config file. Default: ~/.opk/config.yml on linux and %APPDATA%\.opk\config.yml on windows + --configure Apply changes to ssh config and create ~/.ssh/opkssh directory + --create-config Creates a client config file if it does not exist + --disable-browser-open Set this flag to disable opening the browser. Useful for choosing the browser you want to use + -h, --help help for login + -t, --key-type Key Type Type of key to generate (default ecdsa) + --log-dir string Directory to write output logs + --print-id-token Set this flag to print out the contents of the id_token. Useful for inspecting claims + -i, --private-key-file string Path where private keys is written + --provider string OpenID Provider specification in the format: , or ,, or ,,, + --send-access-token Set this flag to send the Access Token as well as the PK Token in the SSH cert. The Access Token is used to call the userinfo endpoint to get claims not included in the ID Token +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_readhome.md b/docs/cli/opkssh_readhome.md new file mode 100644 index 00000000..355f89f2 --- /dev/null +++ b/docs/cli/opkssh_readhome.md @@ -0,0 +1,32 @@ +## opkssh readhome + +Read the principal's home policy file + +### Synopsis + +Read the principal's policy file (/home//.opk/auth_id). + +You should not call this command directly. It is called by the opkssh verify command as part of the AuthorizedKeysCommand process to read the user's policy (principals) home file (~/.opk/auth_id) with sudoer permissions. This allows us to use an unprivileged user as the AuthorizedKeysCommand user. + + +``` +opkssh readhome [flags] +``` + +### Examples + +``` + opkssh readhome alice +``` + +### Options + +``` + -h, --help help for readhome +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/docs/cli/opkssh_verify.md b/docs/cli/opkssh_verify.md new file mode 100644 index 00000000..6b46c74a --- /dev/null +++ b/docs/cli/opkssh_verify.md @@ -0,0 +1,54 @@ +## opkssh verify + +Verify an SSH key (used by sshd AuthorizedKeysCommand) + +### Synopsis + +Verify extracts a PK token from a base64-encoded SSH certificate and verifies it against policy. It expects an allowed provider file at /etc/opk/providers and a user policy file at either /etc/opk/auth_id or ~/.opk/auth_id. + +This command is intended to be called by sshd as an AuthorizedKeysCommand: + https://man.openbsd.org/sshd_config#AuthorizedKeysCommand + +During installation, opkssh typically adds these lines to /etc/ssh/sshd_config: + AuthorizedKeysCommand /usr/local/bin/opkssh verify %%u %%k %%t + AuthorizedKeysCommandUser opksshuser + +Where the tokens in /etc/ssh/sshd_config are defined as: + %%u Target username (requested principal) + %%k Base64-encoded SSH public key (SSH certificate) provided for authentication + %%t Public key type (SSH certificate format, e.g., ecdsa-sha2-nistp256-cert-v01@openssh.com) + +Verification checks performed: + 1. Ensures the PK token is properly formed, signed, and issued by the specified OpenID Provider (OP). + 2. Confirms the PK token's issue (iss) and client ID (audience) are listed in the allowed provider file (/etc/opk/providers) and the token is not expired. + 3. Validates the identity (email or sub) in the PK token against user policies (/etc/opk/auth_id or ~/.opk/auth_id) to ensure it can assume the requested username (principal). + +If all checks pass, Verify authorizes the SSH connection. + +Arguments: + PRINCIPAL Target username. + CERT Base64-encoded SSH certificate. + KEY_TYPE SSH certificate key type (e.g., ecdsa-sha2-nistp256-cert-v01@openssh.com) + +``` +opkssh verify [flags] +``` + +### Examples + +``` + opkssh verify root ecdsa-sha2-nistp256-cert-v01@openssh.com +``` + +### Options + +``` + --config-path string Path to the server config file. Default: /etc/opk/config.yml. (default "/etc/opk/config.yml") + -h, --help help for verify +``` + +### SEE ALSO + +* [opkssh](opkssh.md) - SSH with OpenPubkey + +###### Auto generated by spf13/cobra on 9-Oct-2025 diff --git a/flake.nix b/flake.nix index faed9482..3c56a22e 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,7 @@ opkssh = pkgs.buildGoModule { name = "opkssh"; src = self; - vendorHash = "sha256-vZb94nwfrqoWnFk1STDDiyqGn1Esqz5VvoQemXIzlNg="; + vendorHash = "sha256-AdQtYqhwhkvvr9bbSWylACg75nC/K7hxb13bP5itusM="; goSum = ./go.sum; meta.mainProgram = "opkssh"; }; diff --git a/go.mod b/go.mod index 3709778c..6cbfc34a 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.3.3+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect @@ -64,6 +65,7 @@ require ( github.com/pkg/sftp v1.13.7 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/rs/cors v1.11.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil/v4 v4.25.5 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.6 // indirect diff --git a/go.sum b/go.sum index 57e1c0af..d30db961 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,7 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -158,6 +159,7 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= diff --git a/main.go b/main.go index 0ec3ed82..501b9ddd 100644 --- a/main.go +++ b/main.go @@ -38,6 +38,7 @@ import ( "github.com/openpubkey/opkssh/policy/files" "github.com/spf13/afero" "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" "github.com/thediveo/enumflag/v2" "golang.org/x/term" ) @@ -384,6 +385,28 @@ Arguments: rootCmd.AddCommand(clientCmd) + // genDocsCmd is a hidden command used as a helper for generating our + // command line reference documentation. + genDocsCmd := &cobra.Command{ + Use: "gendocs ", + Hidden: true, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + path := "./docs/cli/" + if len(args) > 1 { + path = args[1] + } + + err := os.MkdirAll(path, 0775) + if err != nil { + return err + } + + return doc.GenMarkdownTree(rootCmd, path) + }, + } + rootCmd.AddCommand(genDocsCmd) + err := rootCmd.Execute() if err != nil { return 1