Technical Documentation: cockpit-project/cockpit
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
What is Cockpit?
Cockpit is an interactive server admin interface. It is easy to use and very lightweight.
Cockpit interacts directly with the operating system from a real Linux session in a browser.
Cockpit makes Linux discoverable, allowing sysadmins to easily perform tasks such as starting
containers, storage administration, network configuration, inspecting logs and so on.
Jumping between the terminal and the web tool is no problem. A service started via Cockpit
can be stopped via the terminal. Likewise, if an error occurs in the terminal, it can be seen
in the Cockpit journal interface.
You can also easily add other machines that have Cockpit installed and are accessible via SSH and jump
between these hosts.
Installation
[!NOTE]
An installation guide can be found at cockpit-project.org/running.html. Some systems may need extra work, read about it in our FAQ
Cockpit is available in numerous Linux distributions and is usually packaged as cockpit.
Flatpak 'Cockpit Client'
We also provide a Flatpak app called Cockpit Client which includes SSH login support to Linux servers.
Screenshots
| Light | Dark |
|---|---|
Contributors
To quickly make code contributions we have a HACKING.md that outlines the devel environment and running tests. For more in-depth we have a developer documentation that links to more resources about setting up the local environment, how designs are proposed, creating and running tests, and our overall contribution process.
Translations are managed in Weblate.
Security
Please see SECURITY.md.
Licenses
Links
- Matrix Channel:
#cockpit:fedoraproject.org - Mailing List
- Guiding Principles
- Release Notes
- Privacy Policy
Overview (`containers/README.md`)
Cockpit Containers
- ws: Cockpit's web server, for installation on CoreOS; uses SSH to connect to the local host or remote machines
- bastion: A reduced variant of the web server that runs unprivileged, and can only connect to remote machines. Suitable for deploying on e.g. Kubernetes. This is currently a prototype.
- unit-tests: Our project's unit tests run in this container; usually on GitHub PRs, but you can also run it locally for reproducing failures.
- flatpak: Scripts for locally building, running, and testing our Cockpit Client flatpak.
See the individual README.md files in the subdirectories for details.
ws container development
Build the container:
make ws-containerYou can install locally built RPMs by copying them into the ws/rpms/
directory.
For fast iteration, you can also build the container with the binaries
(cockpit-ws, cockpit-tls, etc.) and the pages (dist/*) from the local
build tree. For that to work, you have to build the project on the same OS as
the container, and configure with --prefix=/usr. No warranties, you have to
know what you are doing!
make ws-container-build-treeRun the built container and log in interactively as a shell:
make ws-container-shellWhen running docker the 'sudo' command will be used to get necessary
privileges.
Overview (`doc/README.md`)
Cockpit Docs
We have two distinct workflows for our docs, one relates to bundling documentation within Cockpit packages, the other regards online documentation for Cockpit with features for ease-of-use.
Explanation of existing directories and what their purpose is. Mostly follows an Antora designated directory design that is explained in Antora folder structure.
doc/ # All of Cockpit's doc is here; Plugins should be excluded.
├── modules # Antora expects a module structure, irrelevant for AsciiDoctor.
│ ├── guide # Cockpit Guide docs, encompasses everything that relates to Cockpit
│ │ ├── bundling # Specific to AsciiDoctor bundling where we put single-page wrappers
│ │ ├── pages # All pages that relate to Cockpit itself
│ │ └── nav.adoc # Antora navigation file, displays pages from the file as a sidebar navigation for the specific component
│ └── man # Cockpit man pages. Packaged as Manpages with AsciiDoctor, HTML with Antora.
│ ├── pages # All manpages for packages distributed in cockpit
│ ├── partials # Antora-designated folder, partials are imported by AsciiDoc manpages for Bugs, and Author
│ └── nav.adoc # Antora navigation file, displays pages from the file as a sidebar navigation for the specific component
└── antora.yml # Config for Antora to pickup that designates navigation files and attributesAntora folder structure
Since Antora is a versatile documentation tool, they have structured directories for managing the modules and module-specific features - like pages and attachments.
Antora assigns preset, content-specific behavior to the source files stored in the family directories.
https://docs.antora.org/antora/latest/family-directories/
Local testing
We do not need to run Antora from cockpit-docs repo to make updates here, instead you can run Antora from NPM to compile it, then simply open the output or run a http server.
With NPM
npm exec antora generate antora-playbook.ymlWith PNPM
pnpx antora generate antora-playbook.ymlYou'll get an output for site generation completed followed by a file to open. If you run Flatpak you might have to run a simple server instead
python -m http.server -d ./doc/output/antoraOverview (`test/README.md`)
Integration Tests of Cockpit
This directory contains automated integration tests for Cockpit, and the
support files for them. The architecture of the automated integration tests is
described in ARCHITECTURE
To run the tests on Fedora, refer to the HACKING guide for
installation of all of the necessary build and test dependencies. There's
no need to trigger a build manually - the test suite preparation step below
will handle that.
If test failures are encountered that look like they may be related to problems
with nested virtualization, refer to
this Fedora guide
for more details and recommendations on ensuring it is enabled correctly.
Preparation and general invocation
Warning: Never run the build, test, or any other command here as root!
You first need to build cockpit, and install it into a VM:
test/image-prepareThis uses the default OS image. See $TEST_OS below how to select a different one.
See test/image-prepare --help for some special modes, like skipping unit
tests, building overlays, or preparing an image with the
cockpit/ws container instead of RPMs.
See our scenarios in test/run for how they are being used.
In most cases you want to run an individual test in a suite, for example:
test/verify/check-metrics TestCurrentMetrics.testCPUYou can get a list of tests by inspecting the def test* in the source, or by
running the suite with -l/--list:
test/verify/check-metrics -lSometimes you may also want to run all tests in a test file suite:
test/verify/check-sessionTo see more verbose output from the test, use the -v/--verbose and/or -t/--trace flags:
test/verify/check-session --verbose --traceIf you specify -s/--sit in addition, then the test will wait on failure and
allow you to log into cockpit and/or the test instance and diagnose the issue.
The cockpit and SSH addresses of the test instance will be printed:
test/verify/check-session -stYou can also run all the tests, with automatic parallelism:
test/common/run-tests --test-dir test/verifyHowever, this will take really long. You can specify a subset of tests (see--help); but usually it's better to run individual tests locally, and let the
CI machinery run all of them in a draft pull request.
The tests will automatically download the VM images they need, so expect
that the initial run may take a few minutes.
Interactive browser
Normally each test starts its own chromium headless browser process on a
separate random port. To interactively follow what a test is doing:
TEST_SHOW_BROWSER=1 test/verify/check-session --traceYou can also run a test against Firefox instead of Chromium:
TEST_BROWSER=firefox test/verify/check-session --traceSee below for details.
Manual testing
You can conduct manual interactive testing against a test image by starting the
image like so:
bots/vm-run -s cockpit.socket archOnce the machine is booted and the cockpit socket has been activated, a
message will be printed describing how to access the virtual machine, via
ssh and web. See the "Helpful tips" section below.
To login using the web client you have to use the username and password listed below. If you wish to enable root login using the web you can remove root from /etc/cockpit/disallowed-users.
- Username:
admin - Password:
foobar
By default, it's only possible to contact the virtual machine from the host
machine on which it's running. If you want to conduct manual testing from
other devices on your network, set TEST_BIND_GLOBAL=1, for example:
TEST_BIND_GLOBAL=1 bots/vm-run -s cockpit.socket archThis will bind the Cockpit and SSH ports to all interfaces, making it possible
to access a URL like http://yourhost.local:9091/ to test Cockpit from another
machine on your LAN.
Pixel tests
Pixel tests in Cockpit ensure that updates of our dependencies or code changes
don't break the UI: for example slight changes of layout, padding, color and
everything which isn't easily spotted by a human. They also give us confidence
that an update of our UI Framework doesn't introduce changes in how Cockpit
looks.
Pixel tests make a screenshot of a selector and compare it to a known good
reference image. if there is a difference, the test fails and a pixel
difference is shown.
This works as our tests run in the cockpit/tasks container
which pins the browser and font rendering so repeated runs provide the same
pixels. To generate new pixels, this tasks container must be used; your own
browser and font rendering software might generate different results. For more
information read the "introduction blog post".
The test images are stored in a git submodule in the test/reference directory
and be fetched with:
./test/common/pixel-tests updateAs Cockpit tests under multiple distributions and it is not worth the effort to
run pixel tests on every supported distribution we only run them for the
image configured in test/reference-image.
Our tests call Browser.assert_pixels at interesting and strategic places.
This assertion method requires at least a CSS selector and an image title.
Pixel tests are generated in five layouts by default: desktop, medium, mobile,
dark and rtl.
Take a screenshot of the content in #detail-content:
browser.assert_pixels("#detail-content", "filesystem")Take a screenshot of the content in #detail-content and ignore all elements
with a class disk-stats as they change per test run:
browser.assert_pixels("#detail-content", "filesystem", ignore=[".disks-stats"])Take a screenshot of the content in #detail-content and skip it for a
specific layout as it generates unstable pixels:
browser.assert_pixels("#detail-content", "filesystem", skip_layouts=["rtl"])To update pixel tests, locally run the test in the current tasks container, or
create a draft PR and let the tests run for test/reference-image and
afterwards fetch the new pixels:
./test/common/pixel-tests fetch "https://cockpit-logs.us-east-1.linodeobjects.com/<snip>/log.html"Finally, upload the new pixel tests and commit the newly generated submodule commit:
./test/common/pixel-tests pushNote that you have to a part of the Contributors group
to push pixel tests.
Test Configuration
You can set these environment variables to configure the test suite:
TEST_OS: The OS to run the tests in, like "fedora-coreos" or
"debian-testing". See the "cockpit-project/cockpit" section in the
test map
for all supported values. "fedora-44" is the default (TEST_OS_DEFAULTin
bots' constants.py).TEST_BROWSER: What browser should be used for testing. Currently supported
values are "chromium" and "firefox". "chromium" is the default.TEST_SHOW_BROWSER: Set to run browser interactively. When not specified,
browser is run in headless mode. When set to "pixels", the browser will be
resized to the exact dimensions that are used for pixel tests.TEST_TIMEOUT_FACTOR: Scale normal timeouts by given float value. Useful for
slow/busy testbeds or architectures.
See the bots documentation
for details about the tools and configuration for these.
Convenient test VM SSH access
It is recommended to add a snippet like this to your ~/.ssh/config. Then
you can log in to test machines without authentication:
Match final host 127.0.0.2
User root
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
CheckHostIp no
IdentityFile CHECKOUT_DIR/bots/machine/identity
IdentitiesOnly yesYou need to replace CHECKOUT_DIR with the actual directory where you clonedcockpit.git, or bots.git if you have a separate clone for that.
Many cockpit developers take it a step further, and add an alias to
allow typing ssh c:
Host c
Hostname 127.0.0.2
Port 2201The final keyword in the first rule will cause it to be checked (and matched)
after the Hostname substitution in the c rule.
Fast develop/test iteration
Each image-prepare invocation will always start from the pristine image and
ignore the current overlay in test/images. It is thorough, but also rather
slow. If you want to iterate on changing only JavaScript/HTML code, as opposed
to the bridge or webserver, the whole build and test cycle can be done much
faster.
You always need to do at least one initial test/image-prepare $TEST_OS run.
Afterwards it depends on the kind of test you want to run.
Nondestructive tests
Many test methods or classes are marked as @nondestructive, meaning that
they restore the state of the test VM enough that other tests can run
afterwards. This is the fastest and most convenient situation for both
iterating on the code and debugging failing tests.
Start the prepared VM with bots/vm-run $TEST_OS. Note the SSH and cockpit
ports. If this is the only running VM, it will have the ports in the
examples below, otherwise the port will be different.
Then start building the page you are working on
in watch and rsync mode, e.g.
RSYNC=c ./build.js -w users(Assuming the c SSH alias from the previous section and first running VM).
Then you can run a corresponding test against the running VM, with additional
debug output:
TEST_OS=... test/verify/check-users -t --machine 127.0.0.2:2201 --browser 127.0.0.2:9091 TestAccounts.testBasicDestructive tests
Other tests need one or more fresh VMs. Instead of a full test/image-prepare
run (which is slow), you can update the existing VM overlay with updated
bundles. Start the build in watch mode, but without rsyncing, e.g.
./build.js -w storagedand after each iteration, copy the new bundles into the VM overlay:
bots/image-customize -u dist:/usr/share/cockpit/ $TEST_OSThen run the test as you would normally do, e.g.
TEST_OS=... test/verify/check-storage-stratis -t TestStorageStratis.testBasicUse bots/vm-reset to clean up all prepared overlays in test/images.
Debugging tests
If you pass the -s ("sit on failure") option to a test program, it
will pause when a failure occurs so that you can log into the test
machine and investigate the problem.
A test will print out the commands to access it when it fails in this
way. You can log into a running test-machine using ssh. See the
"Helpful tips" section below.
You can also put calls to sit() into the tests themselves to stop them
at strategic places.
That way, you can run a test cleanly while still being able to make
quick changes, such as adding debugging output to JavaScript.
Guidelines for writing tests
If a test is not decorated with @nondestructive, it is OK for a test to
destroy the test machine OS installation, or otherwise modify it without
cleaning up. For example, it is OK to remove all of /etc just to see what
happens. The next test will get a pristine test machine.
Tests decorated with @nondestructive will all run against the same test
machine. The nondestructive test should clean up after itself and restore the
state of the machine, such that the next nondestructive test is not impacted.
A fast running test suite is more important than independent,
small test cases.
Thus, it is OK for tests to be long. Starting the test machine is so slow that
we should run as many checks within a single session as make sense. Note that
nondestructive tests do not suffer from this, and are much quicker.
Still, within a long test, try to have independent sections, where
each section returns the machine to more or less the state that it was
in before the section. This makes it easier to run these sections
ad-hoc when doing incremental development.
Coverage
Every pull request will trigger a $DEFAULT_OS/devel scenario which creates a
coverage report of the JavaScript code executed and writes comments about
uncovered code in the pull request. The overall coverage percentage is recorded
in prometheus for a subset of our projects and visualized in Grafana.
To generate coverage locally for TestApps:
export NODE_ENV=devel
./build.js
./test/image-prepare -q
./test/common/run-tests --test-dir test/verify --coverage TestAppsCode which is impossible or very hard to test in our tests can be excluded from
appearing in a pull request as comment by adding a not-covered comment with a
short justification:
return cockpit.script(data, { superuser: "try", err: "message" })
.catch(console.error); // not-covered: OS errorHelpful tips
For web access, if you'd like to avoid Chromium (or Chrome) prompting
about certificate errors while connecting to localhost, you can change
the following setting:
chrome://flags/#allow-insecure-localhostOverview (`containers/ws/README.md`)
Cockpit webserver container
Cockpit is a web-based graphical interface for Linux servers.
It is packaged in most major Linux distributions.
This container image provides Cockpit's web server and a subset of available pages (like the cockpit-system package)
for deployment on container hosts such as Fedora CoreOS or Kubernetes, where installing rpms is difficult or impossible.
Usage on container host distributions
The standard Fedora and Red Hat Enterprise Linux CoreOS images do not contain Cockpit packages. Thecockpit/ws container includes a minimal set of builtin Cockpit pages which are being used when connecting to
such a machine, i.e. a host which doesn't have the cockpit-bridge package installed.
If these builtin pages are not enough for your use cases, you can install desired Cockpit packages
as overlay RPMs. For example:
rpm-ostree install cockpit-system cockpit-ostree cockpit-podman
rebootDepending on your configuration, you may want to use
other extensions as
well, such as cockpit-podman or cockpit-networkmanager.
If you have a custom-built OSTree, simply include the same packages in your build.
These packages are enough when the CoreOS machine is only connected to through another host running Cockpit.
You also need to run a Cockpit web server somewhere, as the "entry point" for browsers. That can
then connect to the local host or any remote machine via ssh to get a Cockpit UI for that machine.
This web server can be deployed as container. It has two modes, which are described below.
Privileged ws container
Privileged mode provides tight integration with the host: You can use the
host's users and their passwords to log in, and Cockpit's branding will adjust
to the host operating system.
Enable password based SSH logins, unless you only use SSO logins.
From localhost only:bashcat << EOF > /etc/ssh/sshd_config.d/02-enable-passwords.conf # enables Cockpit web login without exposing password auth to network Match Address 127.0.0.1,::1 PasswordAuthentication yes EOFOr from any host:
bashecho 'PasswordAuthentication yes' | sudo tee /etc/ssh/sshd_config.d/02-enable-passwords.conftextsudo systemctl try-restart sshdRun the Cockpit web service with a privileged container (as root):
textpodman container runlabel --name cockpit-ws RUN quay.io/cockpit/wsYou can append additional cockpit-ws CLI options,
most commonly to change the port:textpodman container runlabel --name cockpit-ws RUN quay.io/cockpit/ws -- -p 80Run this to show the "RUN" label command if you want to run the container with different arguments:
textpodman container runlabel --display RUN quay.io/cockpit/wsMake Cockpit start on boot:
textpodman container runlabel INSTALL quay.io/cockpit/ws systemctl enable cockpit.service
Afterward, use a web browser to log into port 9090 on your host IP address as usual.
Unprivileged bastion container
When you run the container without --privileged, it presents an unbranded
login page. The user always has to specify a host name, and it connects to that
host with SSH.
podman run -d --name cockpit-bastion -p 9090:9090 quay.io/cockpit/wsThis mode is suitable for deploying to e.g. Kubernetes or similar environments
where you cannot have or want privileged containers. In this "bastion host
mode", you can get a Cockpit for servers in your data center without opening an
extra port for cockpit-ws on them.
You can still log into the ws container's host if you specify its public
address. podman provides the special namehost.containers.internal for that.
Configuration
By default, the container uses an internal
cockpit.conf
which sets RequireHost = true and a neutral login title. You can customize
this by passing your own configuration as a volume:
-v /path/to/your/cockpit.conf:/etc/cockpit/cockpit.conf:ro,ZSimilarly you can also provide a custom /etc/os-release to change the
branding.
SSH authentication: Share keys with container
The login page asks the user to confirm unknown SSH host key fingerprints. You
can mount your known host keys into the container at/etc/ssh/ssh_known_hosts, or set the environment variableCOCKPIT_SSH_KNOWN_HOSTS_FILE to specify a different location:
-v /path/to/known_hosts:/etc/ssh/ssh_known_hosts:ro,ZYou can also mount encrypted private keys inside the container. You can set an environment variable, COCKPIT_SSH_KEY_PATH_MYHOST, where MYHOST is the uppercased hostname used in the Connect to field, and cockpit will use that private key to login for the specified host. Private keys can be set for multiple hosts this way by changing the value of MYHOST. You can also set an environment variable, COCKPIT_SSH_KEY_PATH, which will be used as a fallback key if no host-specific key is set:
-e COCKPIT_SSH_KEY_PATH_MYHOST=/.ssh/myhost_id_rsa \
-e COCKPIT_SSH_KEY_PATH_MYSERVER=/.ssh/myserver_id_rsa \
-e COCKPIT_SSH_KEY_PATH_192.168.1.1=/.ssh/another_id_rsa \
-e COCKPIT_SSH_KEY_PATH=/.ssh/id_rsa \
-v ~/.ssh/:/.ssh:ro,ZPrivate keys can be encrypted; then cockpit uses the provided password to decrypt the key.
SSH authentication: Share SSH agent with container
Alternatively, if you use ssh-agent on
your host, you can share it with the container and run the container as your
own user (not as system container!). Then logging into remote machines from
Cockpit's login page re-uses the loaded private keys. For that, bind-mount the
agent socket in the container and tell it its path. If your host has SELinux
enabled, you need to disable the isolation for the container, so that it is
allowed to connect to the agent socket:
-v $SSH_AUTH_SOCK:/ssh-agent \
-e SSH_AUTH_SOCK=/ssh-agent \
--security-opt=label=disableMore Info
Overview (`examples/long-running-process/README.md`)
Launch and reattach to a long-running process
Cockpit pages should never spawn long-running precious processes, as a Cockpit
session is not reliably connected for a long time: Network interfaces go down
or roam, TCP timeouts happen, browsers crash, tabs get closed accidentally,
batteries drain, people close/suspend their laptops, and so on.
Most services that Cockpit talks to manage their own runtime state and jobs,
such as udisks, libvirt, or podman. But this is not the case for e.g. running
Ansible playbooks or installer scripts. These should be wrapped into a
transient service unit, so that systemd takes over the role of the job manager.
This example demonstrates how to do that. It runs an arbitrary shell command in
a transient cockpit-longrunning.service, shows the live log output, and
reattaches to it on page load. I.e. you can start the process, log out, log
back in, and the service is recognized as "already running" with the complete
output.
For your own use case, the most important design question is the choice of the
service name, especially if the page can manage more than one process (such as
launching parallel playbooks with different environment variables). It must
contain exactly all the identifying properties in the name, so that it is
predictable.
If your page does manage multiple processes in parallel, it needs to enumerate
running units with ListUnits().
This example page only manages one at a time, and thus just uses a GetUnit() call
with a known name.
Overview (`src/tls/README.md`)
cockpit TLS proxy
cockpit-tls is a TLS terminating HTTP proxy for cockpit-ws. TLS termination
should not be done directly in cockpit-ws, as that is hard to audit and trust:
This code is subject to external attacks through HTTP, does a lot of
interpretation of data streams, uses a lot of external dependencies, and
multiplexes sessions of different users in one process. Thus any vulnerability
can easily lead to complete privilege escalation across all running and future
sessions.
By isolating the TLS termination and certificate checking from the http
processing, and isolating http processing for each client certificate, Cockpit
can do certificate based client authentication (smart cards, browser-imported
certificates, and similar) in a sufficiently robust manner. Any tampering with
a cockpit-ws process is then unable to affect other sessions and the
certificate attestation.
Design goals
- Run TLS termination (cockpit-tls) and the actual data interpretation and
sessions (cockpit-ws) in separate security contexts, so that vulnerabilities
in the HTTP session cannot affect the trust put into the TLS authentication. - Run separate cockpit-ws processes for each TLS client certificate, plus one
instance for "https without certificate", and another one for "unencrypted
http", so that one tampered session cannot affect the others. - cockpit-tls does not interpret the HTTP data stream, to avoid risking any
vulnerability. It is only allowed to inspect the first byte of a new
connection to decide between TLS or plain HTTP, and do the TLS negotiation
and encryption/decryption. In other words, it treats the payload on the TLS
connection as a black box. - Minimal dependencies: Only glibc and a TLS library (GnuTLS at the moment),
so that the code can be audited (by humans or things like coverity) more
easily.
Structure
+---------+ http://machine:9090 +------------------------+
|Browser A|+----------------------+ +-->|cockpit-ws http instance|
+---------+ | | +------------------------+
| |
| + (plain HTTP over Unix socket)
| |
+---------+ https://machine:9090 | +------------+ | +------------------------------------+
|Browser B|+----------------------+--->| cockpit-tls|--+-->|cockpit-ws https instance for cert B|
+---------+ client cert B | +------------+ + +------------------------------------+
| |
| |
+---------+ https://machine:9090 | | +------------------------------------+
|Browser C|+----------------------+ +-->|cockpit-ws https instance for cert C|
+---------+ client cert C +------------------------------------+The startup of these instances and isolating them from one another makes heavy
use of systemd features.
- The top-level unit that the admin enables/controls is cockpit.socket,
which listens to the port (9090 by default). - That activates cockpit.service, which ensures
that a server TLS certificate is present and startscockpit-tls, usually
as usercockpit-ws(for historical reasons, to avoid having to change the
ownership of/etc/cockpit/ws-certs.d/*on upgrades). - When accepting a connection, cockpit-tls checks if it uses TLS:
- If not, it connects to cockpit-wsinstance-http.socket or
cockpit-wsinstance-http-redirect.socket. - If the connection does use TLS, it calculates the fingerprint of the
client certificate (using the empty string if there is none), and connects
to cockpit-wsinstance-https-factory.socket.
This starts a helper factory processcockpit-wsinstance-factorythat
reads the fingerprint from stdin, and asks systemd to start a new
[email protected]
and .service pair.
- If not, it connects to cockpit-wsinstance-http.socket or
- Each instance runs in its own systemd cgroup, as another unprivileged
dynamic system usercockpit-wsinstance-socket. - cockpit-tls exports the client certificates to
/run/cockpit/tls/<fingerprint>
while there is at least one open connection with that certificate, i. e. as
long as there is an active Cockpit session.
Client certificate authentication
This must be explicitly enabled in cockpit.conf with ClientCertAuthentication = yes,
see the guide and
manpage. Then cockpit-tls will ask the
browser for a client certificate.
Commonly this is provided by a smart card, but it's equally possible to import
certificates directly into the web browser.
This requires the host to be in an Identity Management domain like
FreeIPA or Active
Directory, which can associate
certificates to users. See the FreeIPA User Certificates
documentation for details.
The sssd-dbus package must be installed for this to work.
If the web browser presents a client certificate, cockpit-tls will write this
certificate to /run/cockpit/tls. If cockpit-ws sees that cockpit-tls
exports a certificate for its connection (by checking its cgroup instance name,
which is the certificate fingerprint, and checking /run/cockpit/tls for it),
then it will request the tls-cert authentication schema from cockpit-session,
instead of the usual basic or gssapi. cockpit-session then uses the content
of this file to ask sssd to map the certificate to a username. If the mapping
is successful, cockpit-session sets the user name and opens the PAM session
without further authentication.
Code layout
A
Connection(inconnection.[hc]) object represents a single TCP
connection from a client (browser) towards cockpit-tls. Each connection is
handled in its own thread, so that blocked connections cannot starve others.
It has the code for launching ws instances and shoveling data back and forth
between the browser and the ws instance.A
Server(inserver.[hc]) object represents the cockpit-tls logic. It is
a singleton (not instantiated), and mostly split out into a separate object
so that it can be properly unit tested. It maintains some global
configuration, listens to the port, and coordinates the connection threads.
andWsInstanceobjects according to incoming requests.certfile.[hc]deals with exporting current certificates to
/run/cockpit/tls/, and the refcounting from all Connections that belong to a
particular certificate.
The other files are helpers or unit tests.
Overview (`tools/mock-build-env/README.md`)
These are some stub pkg-config and include files that allow us to runmake dist in a restricted build environment without the regular build
dependencies, in particular Packit's sandcastle container.
These are used in packit.yaml.
Doc Anaconda (`doc/anaconda.md`)
Cockpit Storage in Anaconda Mode
Anaconda (the OS Installer) can open the Cockpit "storaged" page for
advanced setup of the target storage devices. When this is done,
storaged is in a special "Anaconda mode" and behaves significantly
different.
In essence, the storaged page restricts itself to working with the
target environment. It will hide the real root filesystem (on the USB
stick that the Live environment was booted from, say), but let the
user create a "fake" root filesystem on some block device.
Entering Anaconda mode
The "storaged" page is put into Anaconda mode by storing a
"cockpit_anaconda" item in its window.sessionStorage. The value
should be a JSON encoded object, the details of which are explained
below.
Ignoring storage devices
Anaconda needs to tell Cockpit which devices can be used to install
the OS on. This is done with the "available_devices" entry, which is
an array of strings.
{
"available_devices": [ "/dev/sda" ]
}This list should only contain entries for top-level block devices. It
should not contain things like partitions, device mapper devices, or
mdraid devices.
Mount point prefix
Cockpit can be put into a kind of "chroot" environment by giving it a
mount point prefix like so:
{
"mount_point_prefix": "/sysroot"
}This works at the UI level: filesystems that have mount points outside
of "/sysroot" are hidden from the user, and when letting the user work
with mount points below "/sysroot", the "/sysroot" prefix is omitted
in the UI. So when the user says to create a filesystem on "/var",
they are actually creating one on "/sysroot/var".
However, Cockpit (via UDisks2) will still write the new mount point
configuration into the real /etc/fstab (not
/sysroot/etc/fstab). This is done for the convenience of Cockpit, and
Anaconda is not expected to read it.
BIOS or EFI
Anaconda needs to tell Cockpit whether a BIOS or a EFI system is being
installed. This controls which kind of special partitions can be
created easily.
This is done by setting the "efi" flag to true or false:
{
"efi": true
}Default filesystem type
Cockpit tries to be smart about which filesystem type to select by
default when formatting something. In normal operation, it will fall
back to the type of the filesystem mounted as "/". When in Anaconda
mode, there might not be anything assigned to "/" yet, and in this
case, Cockpit will use the type from "default_fsys_type".
{
"default_fsys_type": "xfs"
}Exported information
Cockpit maintains some information in the session storage that can be
used by Anaconda to learn things that it doesn't get from blivet. This
is mostly information from fstab.
The "cockpit_mount_points" entry in local storage will have a JSON
encoded object, for example:
{
"/dev/sda": {
"type": "filesystem",
"dir": "/"
},
"/dev/sdb": {
"type": "swap"
},
"/dev/sdc": {
"type": "crypto",
"content": {
"type": "filesystem",
"subvolumes": {
"home": { "dir": "/home" }
}
}
}
}The keys are pathnames of device nodes in /dev.
Each value is an object with a "type" field. The type determines which
other fields might be present, and what they mean. The following
types might appear:
- "filesystem"
A filesystem with an entry in fstab. A filesystem without subvolumes
has a "dir" field that is its mount point. A filesystem with
subvolumes has a "subvolumes" field that is a map from subvolume
names to mount points.
There might also be both a "dir" and a "subvolumes" field. The "dir"
field then has the mount point for the default subvolume of the
filesystem. This is hopefully rare.
- "swap"
A swap device. No other fields are present.
- "crypto"
An encrypted device. It has a "content" field with a value that is
structured like a value for "cockpit_mount_points", i.e., a object
with a "type" field and maybe a "dir" field if "type" is
"filesystem". This might also be present when the crypto device is
closed.
It might also have a "cleartext_device" field if the encrypted device
is currently open.
Cockpit does some magic (via the "x-parent" options in fstab and
crypttab) to produce information also for locked LUKS devices, and
inactive logical volumes.
Cockpit also remembers and exports encryption passphrases in session
storage, in the "cockpit_passphrases" entry. This is a map from device
names to cleartext passphrases. This is only done when Cockpit runs in
a "secure context", see
https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
Cockpit Networking in Anaconda Mode
The Cockpit networking page behaves differently when it is
used in Anaconda mode. Instead of the full networking page it shows a
simplified layout that is adjusted for the requirements of the Anaconda
installer.
Entering Anaconda mode
Anaconda mode is activated the same way as on the storage page, by
storing a "cockpit_anaconda" item in window.sessionStorage. The value
should be a JSON encoded object. Unlike on the storage page, none of the
contents are actually used by the networking page, so setting any valid
JSON encoded object is enough to activate the Anaconda layout.
The code for this alternative layout lives inpkg/networkmanager/anaconda and is a simplified version of the regular
networking page.
Doc Antora.Yml (`doc/antora.yml`)
name: cockpit-guide
version: true
title: Cockpit
start_page: guide:index.adoc
nav:
- modules/guide/nav.adoc
- modules/man/nav.adoc
Doc Authentication (`doc/authentication.md`)
Cockpit Authentication
Cockpit authorizes users by looking at the Authorization header on requests
to /login. Cockpit will attempt to perform the start the authentication command that
is configured for the auth scheme contained in the header.
To configure an auth scheme add a section to cockpit.conf for that scheme. For example
to configure an command for the "Bearer" auth scheme your cockpit.conf should contain
the following section.
[bearer]
command = example-verify-token
timeout = 300The command is then responsible to:
- verify the given credentials
- setup an appropriate session and environment based on those credentials
- launch a bridge that speaks the cockpit protocol on stdin and stdout.
The default command is cockpit-session it is able to handle Password
([basic]), Kerberos/GSSAPI ([negotiate]), and TLS client certificate
([tls-cert]) authentication.
Authentication commands are called with a single argument which is the host that the user
is connecting to. They communicate with their parent process using the cockpit protocol on
stdin and stdout.
Instead of Command, it's also possible to specify UnixPath to connect to a unix socket at a
given path. The protocol is the same.
Credentials can then be retrieved by issuing a authorize command with a challenge. The challenge
should correspond to the authorization type in header (ei: Basic or Bearer). For example:
{
"command": "authorize",
"cookie": "cookie",
"challenge": "*"
}The response will look something like this
{
"command": "authorize",
"cookie": "cookie",
"response": "Basic dXNlcjpwYXNzd29yZAo=",
"remote-peer": "1.2.3.4",
}remote-peer is the IP address of the connecting user, if known (otherwise
unset).
A * challenge requests whatever credentials the parent process has. Most auth
commands will want to begin by issuing a * challenge.
By default cockpit-ws will wait a maximum of 30 seconds to receive this response.
The number of seconds to wait can be adjusted by adding a timeout parameter along
side the auth schema configuration in your config file. The given value should be
a number between 1 and 900.
If more information is needed the command should respond with a X-Conversation challenge.
This takes the following format.
X-Conversation nonce base64(prompt message)The message will be displayed to the user and the user will be prompted for a response.
If the user does not respond within 60 seconds the command will be closed and the login
aborted. The number of seconds to wait can be adjusted by adding a response-timeout parameter
along side the auth schema configuration in your config file. The given value should be a
number between 1 and 900.
Once a result is known a "init" command should be sent. If the login was successful you can usually
just let the bridge do this.
If the login was not successful the JSON should include a problem field. Values ofauthentication-failed, authentication-unavailable or access-denied
are translated to the appropriate cockpit error codes. Any other values are treated
as generic errors. Additionally a message field may be included as well.
If an process exits without sending a init command, that will be treated as an internal error.
If the authentication command has additional data that it would like to return with a successful response
it can do so by sending a x-login-data challenge. The command should have an additional JSON fieldlogin-data. The string placed there will be returned by along with a successful json response.
For a simple python example see
cockpit-auth-ssh-key.
Remote machines
Cockpit also supports logging directly into remote machines. The remote machine to
connect to is provided by using a application name that begins with cockpit+=.
The default command used for this is python3 -m cockpit.beiboot, which
invokes ssh.
The section Ssh-Login defines the options for all ssh commands. The section
has the same options as the other authentication sections with the following additions.
hostThe default host to log into. Defaults to 127.0.0.1. That host's key
will not be checked/validated.connectToUnknownHosts. By default cockpit will refuse to connect to any machines that
are not already present in ssh's globalknown_hostsfile (usually/etc/ssh/ssh_known_hosts). Set this totrueis to allow those connections
to proceed.
After the user authentication with the "*" challenge, if the remote
host is not already present in any local known_hosts file, this will send an"x-host-key" challenge:
{
"command": "authorize",
"challenge": "x-host-key"
"cookie": "cookie",
}The caller responds to that with either a valid key like below, or an empty
string response if there is no available key.
{
"command": "authorize",
"cookie": "cookie",
"response": "ssh-rsa AAAA1234...",
}Client certificate authentication
When a machine is joined to an Identity Management domain (like
FreeIPA or Active Directory) which has client-side
user certificates set up,
then these can be enabled for authentication to Cockpit by setting this option
in cockpit.conf:
[WebService]
ClientCertAuthentication = yesThis uses the [tls-cert] authentication scheme.
When enabling this mode, other authentication types commonly get disabled. See
the next section for details.
See the Certificate/smart card authentication guide
for details how to set this up.
Actions
Setting an action can modify the behavior for an auth scheme. Currently two actions
are supported.
- remote-login-ssh Use the
Ssh-Loginsection instead. - none Disable this auth scheme.
To configure an action add the action option. For example to disable basic authentication,
cockpit.conf should contain the following section:
[basic]
action = noneLikewise, if the machine is part of a Kerberos domain, but that should not be used to
authenticate to cockpit, create the following section:
[negotiate]
action = noneLimits
Cockpit can be configured to limit the number of concurrent login processes. See cockpit.conf
for more details. This will affect how many custom authentication processes can be launched.
Environment Variables
The following environment variables are set by cockpit-ws when spawning an auth process for
SSH connections:
COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS Set to
1to allow connecting to
hosts that are not present in the currentknown_hostsfiles. If not set,
Cockpit will only connect to unknown hosts if either the remote peer is
local, or if theSsh-Loginsection incockpit.confhas anconnectToUnknownHostsoption set to a true value (1,yesortrue).COCKPIT_SSH_KNOWN_HOSTS_FILE Path to knownhost files. Defaults to
PACKAGE_SYSCONF_DIR/ssh/ssh_known_hosts
Login process: User/Password
This section entirely ignores cockpit-tls for simplicity, as it is not involved in the authentication. See cockpit-tls docs for details.
- User connects to cockpit URL.
- cockpit-ws responds with "401 Authentication failed" and sends the Login page, unless when Kerberos is available (see below) or the server is configured to not use any authentication.
- User fills in username/password and clicks "Log In". The login page sends a new request to cockpit-ws with an
Authorization: Basic base64(user:password)header - cockpit-ws looks at
cockpit.confwhether it has a customized sessionCommandorUnixPathforbasic. If not, defaults tocockpit-session(viaUnixPath = /run/cockpit/session). It parses user/password from the header and spawns the session command with the target host as argument. - cockpit-session sends an
authorizecommand with a*challenge (see above) to cockpit-ws, which responds with the user/password - cockpit-session starts a PAM session for the user, and sets the initial credential to the received password
- If PAM sends more messages, like e.g. 2FA prompts or changing expired passwords:
- cockpit-session sends corresponding
X-Conversationauthorize messages (see above) to cockpit-ws - cockpit-ws forwards them to the Login page, which displays the text, and sends the user response as the authorize reply
- cockpit-ws forwards the authorize reply to PAM
- cockpit-session sends corresponding
- When PAM succeeds, cockpit-session executes the bridge and the session starts.
sequenceDiagram
participant User
participant cockpit-ws
participant cockpit-session
participant PAM
User->>cockpit-ws: GET https://server:9090
cockpit-ws->>User: 401 + Login page
User->>cockpit-ws: GET with Authorization: Basic
cockpit-ws->>cockpit-session: Spawn
cockpit-session->>cockpit-ws: authorize command with * challenge
cockpit-ws->>cockpit-session: user/password
cockpit-session->>PAM: pam_authenticate()
PAM-->>cockpit-session: Success or conversation
opt 2FA/password change
cockpit-session->>cockpit-ws: X-Conversation challenge
cockpit-ws->>User: Display prompt
User->>cockpit-ws: Response
cockpit-ws->>cockpit-session: Forward response
cockpit-session->>PAM: Continue conversation
end
PAM->>cockpit-session: Success
cockpit-session->>PAM: open_session()
cockpit-ws->>User: 200 OK for the login pageLogin process: Kerberos/GSSAPI
- User connects to cockpit URL.
- cockpit-ws responds with "401 Authentication failed" and includes
WWW-Authenticate: Negotiateheader (if Kerberos is available) - Browser (if configured for SPNEGO/Kerberos) requests a service ticket from the KDC for the HTTP service principal
- Browser sends a new request with
Authorization: Negotiateheader - cockpit-ws looks at
cockpit.confwhether it has a customized session command fornegotiate. If not, defaults tocockpit-sessionand runs it in the same way as above. - cockpit-session calls
gss_accept_sec_context()with the GSSAPI token to verify the Kerberos ticket - If GSSAPI returns
GSS_S_CONTINUE_NEEDED(multi-round negotiation):- cockpit-session sends an authorize command with a
Negotiatechallenge containing the output token to cockpit-ws - cockpit-ws responds with "401 Authentication failed" and
WWW-Authenticate: Negotiate <token>to the Browser - Browser sends another
Authorization: Negotiate <token>request - This continues until GSSAPI negotiation completes
- cockpit-session sends an authorize command with a
- When GSSAPI succeeds, cockpit-session has the authenticated GSSAPI principal name
- cockpit-session maps the GSSAPI name to a local username using
gss_localname()(which applies configured mapping rules), or if that fails, falls back togss_display_name()which returns the principal name as-is (e.g.[email protected]) - cockpit-session starts PAM, skipping the auth stack (as GSSAPI already authenticated), and runs the account, credential, and session stacks
- cockpit-session stores the delegated Kerberos credentials (if delegation was negotiated) in a credential cache at
/run/user//cockpit-session-.ccacheand setsKRB5CCNAMEin the PAM environment, so that the bridge can use them for accessing other Kerberos-protected services (like SSH to remote machines) - Authentication completes, and the session starts as above.
Login process: Client Certificate
Unlike the other sections, this one involves cockpit-tls as well as it provides a crucial part of the authentication.
- User connects to cockpit URL with a client certificate, which lands at cockpit-tls
- cockpit-tls calculates the SHA256(certificate) as the user fingerprint
- cockpit-tls connects to the
cockpit-wsinstance-https@<fingerprint>systemd socket/service (starting a dedicated cockpit-ws instance for this certificate if needed). See cockpit-tls docs and systemd units for details. - cockpit-tls exports the certificate to
/run/cockpit/tls/clients/<fingerprint>(kept as long as there is at least one active connection with that certificate) - cockpit-tls includes
"client-certificate": "<fingerprint>"in its mini JSON protocol to cockpit-ws - cockpit-ws detects the client certificate metadata and uses
tls-cert <fingerprint>as the authorization type - cockpit-ws looks at
cockpit.confwhether it has a customized session command fortls-cert. If not, defaults tocockpit-sessionand runs it in the same way as above. - cockpit-session receives the
tls-cert <fingerprint>authorization and reads the certificate from/run/cockpit/tls/clients/<fingerprint> - cockpit-session validates that the certificate file exists and matches the expected cockpit-ws cgroup
- cockpit-session calls the sssd D-Bus API (
org.freedesktop.sssd.infopipe.Users.FindByCertificate) to map the certificate to a username - When successful, cockpit-session sets the username and starts PAM, skipping the auth stack (as the certificate itself was the authentication), and runs the account, credential, and session stacks
- Authentication completes, and the session starts as above.
Login process: SSH to remote machine
- User connects to a URL like
https://server:9090/=hostname) or sets the "Connect to:" field in the Login page tohostname. - login.js sends the login HTTP request with
Authorization: Basic base64(user:password\0known_hosts)header, whereknown_hostscontains any previously-stored SSH host keys for the target host from the browser'slocalStorage - cockpit-ws extracts the target host from the URL. As it has a host name, it looks at
cockpit.conffor the[Ssh-Login]section'sCommandorUnixPath. If not customized, defaults tocockpit.beiboot - cockpit-ws spawns the ssh command with the target host as argument
- cockpit.beiboot sends an
authorizecommand with a*challenge to cockpit-ws, which responds with the user/password/known_hosts from theAuthorizationheader (same as User/Password step 5) - cockpit.beiboot parses the credentials, writes any received
known_hoststo a temporary file, and configuressshto use it via-o UserKnownHostsfile=... - cockpit.beiboot connects to the remote host via SSH using the ferny API.
- If the remote host's SSH key is unknown or has changed:
- For unknown hosts: SSH prompts via its
SSH_ASKPASSmechanism. ferny's interaction agent detects the host key prompt, parses the fingerprint, and cockpit.beiboot sends anX-Conversationchallenge to cockpit-ws with the host key fingerprint and hostname. - For changed keys: SSH immediately fails with a changed host key error. ferny detects this as
SshChangedHostKeyError. cockpit.beiboot fails withproblem=invalid-hostkey. login.js catches this and retries the login without sending the old known_hosts entry, causing SSH to treat it as an unknown host (see above). - For localhost (127.0.0.1): cockpit.beiboot automatically accepts the key without user interaction.
- cockpit-ws forwards the
X-Conversationchallenge to the web UI - login.js shows a host key verification dialog with the fingerprint, key type, and hostname to the user; it remembers unknown vs. changed and shows appropriate UI
- User accepts or rejects the key
- login.js sends the response back via
X-Conversationheader - cockpit.beiboot returns the response to SSH's askpass mechanism
- SSH proceeds with the connection (if accepted) or fails (if rejected)
- For unknown hosts: SSH prompts via its
- If SSH prompts for additional input (like 2FA): Similar to step 7 in User/Password section: cockpit.beiboot sends
X-Conversationmessages back and forth - When SSH authentication succeeds, cockpit.beiboot either runs an existing remote
cockpit-bridge, or sends its own Python module through the SSH connection (via beipack). It starts bridge on the remote machine and connects its stdio to it - Authentication completes, and the session starts as above.
- After successful SSH authentication and bridge startup, if a new host key was accepted, cockpit.beiboot reads the updated
known_hostsfile and sends it to the browser in theinitmessage'slogin-datafield asknown-hosts - login.js stores the received
known-hostsentry in localStorage for future connections to this host
Doc Branding (`doc/branding.md`)
Branding
Typically Cockpit is branded in such a way that it looks like the admin
user interface for an operating system. The "Cockpit" brand is only used
during development.
Due to trademark law, as a general rule Cockpit does not ship logos for
operating systems in its packaging. These are expected to be present on the
system itself, and are incorporated into the branding.
How Cockpit Selects Branding
In $prefix/share/cockpit/branding are multiple directories, each of which
contain branding information. Branding files are served from the directories
based in the order below, if a file is not present in the first directory on
the list, the second will be consulted, and so on.
$prefix/share/cockpit/branding/$ID-$VARIANT_ID
$prefix/share/cockpit/branding/$ID
$prefix/share/cockpit/branding/default
$prefix/share/cockpit/staticThe $ID and $VARIANT_ID variables are those listed in /etc/os-release,
and $prefix is usually /usr.
Administrators can provide a custom local branding in /etc/cockpit/branding/
(more generally, $XDG_CONFIG_DIRS/cockpit/branding/). If that directory
exists, it is considered before the above directories.
All of the files served from these directories are available over HTTP
without authentication. This is required since these resources will be used
on the login screen.
Branding files
The following files are interesting in the above directories for branding
purposes:
apple-touch-icon.png
favicon.ico
branding.cssIn addition there are image files referred to by branding.css (see below).
Since Cockpit does not package trademarked logos, typically there will be
symlinks from a branding directory to the relevant image files elsewhere
on the system.
Branding Styles
The Cockpit login screen and navigation area loads a branding.css file
from the above directories.
The branding.css file should override the following areas of the login screen:
/* Background of the login prompt */
html body.login-pf {
background: url("my-background-image.jpg");
background-size: cover;
}
/* Upper right logo of login screen */
#badge {
width: 225px;
height: 80px;
background-image: url("logo.png");
background-size: contain;
background-repeat: no-repeat;
}
/* The brand text above the login fields */
#brand::before {
font-size: 18pt;
text-transform: uppercase;
content: "${NAME} <b>${VARIANT}</b>";
}Notice how we can use variables from /etc/os-release in the branding.
The value for these variables come from the machine that cockpit is logged into.
branding.css may also set the --ct-color-host-accent CSS variable to change
the Cockpit Shell panel's accent color. You can also customize
PatternFly's brand colors
and other PatternFly variables. For example:
:root {
--ct-color-host-accent: #c00 !important;
--pf-t--global--color--brand--default: red;
}Doc I18n (`doc/i18n.md`)
Cockpit Internationalization
All human-visible strings on Cockpit's web pages should be translatable. This
document describes the entire internationalization process, which uses GNU gettext and related tools as much as possible.
Marking strings
Strings that are visible to humans and which should be translated must be
marked as such on Cockpit pages, in one of two ways:
If the string appears in a HTML source, it must be enclosed in a tag with
thetranslate="yes"attribute. Example:html<span translate="yes">Image</span>If the string appears in JavaScript source, it must use the
cockpit.gettext()
or a related function, commonly called through an alias_(), with double-quoted
strings so thatxgettextcan recognize it. Example:jsconst _ = cockpit.gettext; var translated = _("Time"); // in a JSX block: <tr> <th>{ _("Image") }</th> </tr>For strings that involve numbers you need to use
cockpit.ngettext()
instead:jsvar translated = cockpit.ngettext("This thing", "The things", numberOfThings);The cockpit.format()
function is useful for parameterized strings where the order of parameters
might change between languages.
Translation process
Translatable strings from HTML, JavaScript (as above) and C files get
extracted with various invocations of
xgettext, see
po/Makefile.am.
Runningmake po/cockpit.potwill generate a standard PO template.With
make upload-potthe PO template gets uploaded to the
Weblate translation platform
where everybody can contribute translations to various languages.With
make download-poWeblate's translations are downloaded topo/XX.po. This is done by
bots/po-refresh
which is invoked regularly by
bots/po-trigger.Translations can also be done using any other tool or platform and be
submitted via pull requests that update thepo/XX.pofiles. In this case
they should then also be uploaded to Weblate either manually using Weblate UI
or by pushing these files into cockpit-weblate
repository.
Using translations at runtime
HTML and JavaScript don't directly support gettext po and mo files. The
translations from po files are converted to a JavaScript function by
cockpit-po-plugin
which inserts the actual translations into a
cockpit.locale() call.
The resulting files are written to dist//po.XX.js from where they
can be imported by the page and used by cockpit.gettext().
HTML pages which use translate="yes" tags need to call
cockpit.translate() which will iterate over all such tags and replace their content with the result of calling cockpit.gettext() on their original content.
Opening a new branch
Occasionally we create a new upstream branch for e. g. a new stable RHEL
release. Run these steps to initialize translations for it:
In cockpit-weblate create a new branch
with the same name.
Upload therecockpit.potthat you generated from the new upstream branch withmake po/cockpit.pot.
If you have also.pofiles, you can also add them into this branch.On the Weblate Cockpit project page,
click the "Add new translation component" button and create a new component. Give it a name
that matches the new branch. Point it to cockpit-weblate
repository to a branch you created in the previous step.On the new cockpit branch, change
WEBLATE_REPO_BRANCHin
po/Makefile.am
from "main" to the new branch name.
Doc Key Auth (`doc/key-auth.md`)
Cockpit Key Based Authentication
Cockpit allows you to monitor and administer several servers at the same time.
While you will need to connect to the primary server with a password
or kerberos ticket, cockpit does support accessing secondary machines via
public key based authentication.
Note that when a user is authenticated in this way the authentication happens
without a password, as such the standard cockpit reauthorization mechanisms do
not work. The user will only be able to obtain additional privileges if they do not require a password.
In order to support key based authentication cockpit adds pam_ssh_add.so
to it's pam stack. Once a user is successfully logged in a new ssh-agent
is started and ssh-add is run to load the default keys, if a password is
requested the one the user logged in with is provided.
When cockpit attempts to establish an ssh connection to a new server,
the running ssh-agent is used to offer any loaded public keys to the
remote server as authentication options.
When the user logs out or has their session terminated, the ssh-agent is also
terminated.
Doc Login (`doc/login.md`)
Cockpit Login
The cockpit login page signs users in by sending requests to /login the a Authorization header.
Generally this will be with a user provided username and password. In that case the authorization
header is built like this.
Basic base64(user:password)A successful response is a 200 http code with a json body that contains a user field with the user
name of the user that was just logged in. Additional fields may be present
Other http codes are considered errors. Generally these are 401 or 403 http status codes.
In most cases the error can detrived from the status text. Examples areauthentication-failed, authentication-unavailable or access-denied
In some cases additional error messages may be included.
In some authentication setups additional steps are required. When this happens cockpit will
return a 401 http status code. Along with a WWW-Authenticate challenge header that looks like
this.
WWW-Authenticate X-Conversation id base64(prompt)This may be accompanied by a JSON object in the body of the response to provide additional context.
{
"message" : ...
"error": ...,
"echo": true or false
}Cockpit will then display the prompt and provide a field for the user to type the answer.
To send the answer the Authorization header is built like this.
Authorization: X-Conversation id base64(response)Where id is the same id that was received with the WWW-Authenticate challenge header.
This can continue until either a success or an error response is received.
Doc Protocol (`doc/protocol.md`)
Protocol Documentation
The protocol is a simple transport for other APIs. It's a simple wrapper over
some other protocols, like DBus, REST, and so on. At the present time this
is unstable, but will need to become stable in the near future.
Channels
Each message is part of a channel, which has a id. This channel id
identifies the type of data in the message payload, the host where it is
headed to/from and things like the service or user credentials.
An empty/missing channel id represents a special channel called the control
channel. It contains command messages. These are described below. Initially
only the control channel exists. Additional channels are opened and closed
via command messages.
Channels operate across all participants, and they help a forwarding layer
like cockpit-ws to know where to send messages.
Framing
The channel id is a UTF-8 valid string. The channel id string comes first
in the message, then a new line, then payload.
For example if you had a payload of the string 'abc', and it was being
sent through the channel 'a5', that might look like:
a5\nabcWhen a message is sent over a stream transport that does not have distinct
messages (such as SSH or stdio), the message is also prefixed with a base 10
integer and new line, which represents the length of the following message.
The length must be a positive number in canonical form (ie: strictly greater
than zero, with no leading zeros).
An example. When going over a stream transport with a payload of the 3 byte
string 'abc', and a channel of 'a5', would have a message length of 6: 3 bytes of
payload, 2 bytes for the channel number, 1 for the new line. It would look like
this in a stream:
6\na5\nabcControl Messages
Control messages let the various components such as cockpit-web, cockpit-ws and
cockpit-bridge communicate about what's going on.
Control messages are always sent in the control channel. They always have an
empty channel. The payload of a control message is always a json
object. There is always a "command" field:
{
"command": <command>,
"channel": <channel>,
...
}If a control message pertains to a specific channel it has a "channel" field
containing the id of the channel. It is invalid to have a present but empty
"channel" field.
Unknown control messages are ignored. Control messages that have a channel are
forwarded to the correct endpoint. Control messages without a channel are not
forwarded, but only travel a single hop.
Command: init
The "init" command is the first message sent over a new transport. It is
an error if any other message is received first. The transport is not considered
open until the "init" message has been received.
The following fields are defined:
- "version": The version of the protocol. Currently 1, and stable.
- "capabilities": Optional array of strings advertizing capabilities.
- "channel-seed": A seed to be used when generating new channel ids.
- "host": The host being communicated with.
- "problem": A problem occurred during init.
- "csrf-token": The web service will send a csrf-token for external channels.
- "os-release": The bridge sends fields from /etc/os-release which identify the system.
- "packages": The bridge sends a list of package names on the system.
- "superuser": Instructs a bridge about whether and how to start a superuser bridge.
If a problem occurs that requires shutdown of a transport, then the "problem"
field can be set to indicate why the shutdown will be shortly occurring.
The "init" command message may be sent multiple times across an already open
transport, if certain parameters need to be renegotiated.
Command: open
The "open" command opens a new channel for payload.
The following fields are defined:
- "binary": If present set to "raw"
- "channel": A uniquely chosen channel id
- "payload": A payload type, see below
- "host": The destination host for the channel, defaults to "localhost"
- "user": Optional alternate user for authenticating with host
- "superuser": Optional. Use "require" to run as root, or "try" to attempt to run as root.
- "group": An optional channel group
- "capabilities": Optional, array of capability strings required from the bridge
- "session": Optional, set to "private" or "shared". Defaults to "shared"
- "flow-control": Optional boolean whether the channel should throttle itself via flow control.
- "send-acks": Set to "bytes" to send "ack" messages after processing each data frame
If "binary" is set to "raw" then this channel transfers binary messages.
After the command is sent, then the channel is assumed to be open. No response
is sent. If for some reason the channel shouldn't or cannot be opened, then
the recipient will respond with a "close" message.
The channel id must not already be in use by another channel.
An example of an open:
{
"command": "open",
"channel": "a4",
"payload": "stream",
"host": "localhost"
}This message is sent to the cockpit-bridge.
The caller can control whether to open a new bridge session or not by setting
the "session" field to "private" or "shared". A "private" session will always
start another bridge just for this channel. If this option is not specified
then typically the channel will share an already existing session. There are
certain heuristics for compatibility, where it defaults to private.
The open fields are also used with external channels. External channels are
channels that have their payload sent via a separate HTTP request or another
WebSocket. In this case the "channel" field must not be present, and an
"external" field may be present with the following arguments:
- "content-disposition": a Content-Disposition header for GET responses
- "content-type": a Content-Type header for GET responses
- "protocols": an array of possible protocols for a WebSocket
Channel "group" fields can be used to group channels into groups. You should
prefix your groups with a reverse domain name so they don't conflict with
other or Cockpit's group names. Group names without any punctuation are
reserved.
One such special group name is "default", which contains all channels which
were opened without specifying a group.
The "flow-control" option controls whether a channel should attempt to throttle
itself via flow control when sending or receiving large amounts of data. The
current default (when this option is not provided) is to not do flow control.
However, this default will likely change in the future. This only impacts data
sent by the bridge to the browser.
If "send-acks" is set to "bytes" then the bridge will send acknowledgement
messages detailing the number of payload bytes that it has received and
processed. This mechanism is provided for senders (ie: in the browser) who
wish to throttle the data that they're sending to the bridge.
Host values
Because the host parameter is how cockpit maps url requests to the correct bridge,
cockpit may need to additional information to route the message correctly.
For example you want to connect to a container my-container running
on "my.host".
To allow this the host parameter can encode a key/value pair that will
be expanded in the open command json. The format is host+key+value. For example
{
"command": "open",
"channel": "a4",
"payload": "stream",
"host": "my.host+container+my-container"
}will be expanded to
{
"command": "open",
"channel": "a4",
"payload": "stream",
"host": "my.host",
"host-container": "my-container",
"host": "my.host"
}Command: close
The "close" command closes a channel.
The following fields are defined:
- "channel": The id of the channel to close
- "problem": A short problem code for closure, or not present for a normal close
The channel id must be set. An example of a close:
{
"command": "close",
"channel" : "5x",
"problem": "access-denied"
}Any protocol participant can send this message. The cockpit-bridge and cockpit-ws
backends will send this message when a channel closes whether because of an
error or a normal closure. The frontend cockpit-web will send this when it
wants to close a channel normally. Once either end has sent this message the
channel is considered closed.
See below for a list of problem codes.
When a channel closing is the result of a ssh transport closing cockpit-ws
reports on the authentication methods used while attempting to authenticate
by adding a "auth-method-results" object to the close object. This is mainly
useful to provide helpful error messages and suggestions to users.
The "auth-method-results" object contains a key for each method that cockpit-ws
is able to attempt authentication with as well as the result of the attempt.
For example:
{
"password": "denied"
}This possible "result" values are:
- "no-server-support": The target server does not support this method.
- "not-provided": cockpit-ws doesn't have a credential to try so this method was skipped.
- "succeeded": Authentication with this method was successful.
- "denied": Authentication with this method was denied.
- "partial": The server wants more authentication.
- "error": Unexpected error occurred when using this method.
- "not-tried": This methods was not tried, usually due to an earlier method succeeding.
Other fields may be present in a close message.
Command: ready
The "ready" command indicates that the channel implementation (usually
the bridge) is in a ready and settled state. It is not normally necessary to
listen to this control message, since it is possible to start sending payload
over a channel immediately after the open message has been sent.
The following fields are defined:
- "channel": The id of the channel
Other fields may be present in a ready message.
Command: done
The "done" command indicates that no more messages will be sent on the channel
in the same direction as the "done" was sent.
The following fields are defined:
- "channel": The id of the channel
Either or both endpoints of a channel can send this message. It may only be
sent once.
After it is sent no more messages may be sent in that direction. It is an error
to send further messages, or send another "done" message.
Command: options
The "options" command sends further channel options on the fly. The contents of
the message depends on the channel payload.
The following fields are defined:
- "channel": The id of the channel
Command: ping
The "ping" command is simply a keep alive.
No additional fields are defined, but any are allowed. If a "channel"
is set this "ping" will be forwarded. Otherwise it be limited to a single hop.
An example of a ping:
{
"command": "ping"
}Any protocol participant can send a "ping". It is responded to by sending
a "pong" with identical options as a reply. If a "ping" is sent with a
"channel" field set to a channel that is not currently open, the "ping"
will be ignored as expected, and no "pong" will be sent.
Command: authorize
The "authorize" command is for communication of authentication and
authorization challenges and responses between cockpit-bridge and
cockpit-ws. Despite the name this is also used for authentication.
For challenge/response authentication, the following fields are defined:
- "cookie": a unique string sent with a challenge that must be
present in the corresponding response. - "challenge": a challenge string present in messages from a bridge
to cockpit-ws - "response": a response string present in messages from cockpit-ws
to a bridge
Example authorize challenge and response messages:
{
"command": "authorize",
"cookie": "555",
"challenge": "Basic c2NydWZmeTp6ZXJvZw=="
}{
"command": "authorize",
"cookie": "555",
"challenge": "Basic c2NydWZmeTp6ZXJvZw=="
}Authorize messages are used during authentication by authentication
commands like cockpit-session to obtain the users credentials
from cockpit-ws. An authentication command can send a authorize message
with a response but no cookie. For example
{
"command": "authorize",
"response": "Basic ..."
}In that case cockpit-ws will store the response and use it in a reply
to a subsequent challenge.
For credential cache authorization, the following fields are defined:
- "credential": Empty or "password"
When set to "password" then a "password" field should also be present
which represents a new password to cache, which replaces any current
passwords. If no password field is present then the passwords are cleared.
Command: kill
The "kill" command terminates a whole set of channels. It is sent by the frontend
and processed by cockpit-ws.
The following fields are defined:
- "host": optional string kills channels with the given host
- "group": optional string to select only channels opened with the given "group"
If no fields are specified then all channels are terminated.
Command: logout
The "logout" command is sent by the shell to cockpit-ws. It
disconnects the user.
In older versions, the "logout" command was broadcast to all bridges
and used to terminate privileged ones. Privileged bridges are now
terminated by closing their transport.
Command: hint
The "hint" command provides hints to other components about the state of things
or what's going to happen next. The remainder of the fields are extensible.
Payload: null
A channel opened with this payload type will never send data, and will
ignore all data it receives.
Payload: echo
A channel opened with this payload type will send back all data that
it receives. It sends an "done" when it receives one.
Payload: dbus-json3
DBus messages are encoded in JSON payloads by cockpit-web, and decoded in
cockpit-bridge. The 'dbus-json1' and 'dbus-json2' protocols are no
longer supported.
Additional "open" command options are needed to open a channel of this
type:
- "bus": The DBus bus to connect to either "session", "system" or "none".
Defaults to "system" if not present. If set to "none" you must also
provide the address parameter. - "name": A service name of the DBus service to communicate with by default.
Always omit this field if "bus" is "none". If omitted and "bus" is not
"none" then a "name" field must be specified explicitly on other messages. - "address": A dbus supported address to connect to. This option is only
used when bus is set to "none". Accepts any valid DBus address or
"internal" to communicate with the internal bridge DBus connection.
The DBus bus name is started on the bus if it is not already running. If it
could not be started the channel is closed with a "not-found". If the DBus
connection closes, the channel closes with "disconnected".
DBus messages are encoded as JSON objects. If an unrecognized JSON object
is received, then the channel is closed with a "protocol-error".
Method calls are a JSON object with a "call" field, whose value is an array,
with parameters in this order: path, interface, method, in arguments.
{
"call": [ "/path", "org.Interface", "Method", [ "arg0", 1, "arg2" ] ],
"id": "cookie"
}All the various parameters must be valid for their use. arguments may be
null if no DBus method call body is expected. If a "type" field is specified
then it is expected to be the DBus method type signature (no tuple). If a
"flags" field is a string, then this includes message flags. The default flags
option has the "i" flag set:
- "i": Allow interactive authentication.
If no "name" field was specified in the "open" call, indicating which DBus
service to talk to, then one must be specified here along with the "call".
If a DBus method call fails an "error" message will be sent back. An error
will also be sent back in parameters or arguments in the "call" message are
invalid.
An optional "id" field indicates that a response is desired, which will be
sent back in a "reply" or "error" message with the same "id" field.
If an interface has been exported, then the bridge will send "call" messages
in its direction for calls received for on that interface.
Method reply messages are JSON objects with a "reply" field whose value is
an array, the array contains another array of out arguments, or null if
the DBus reply had no body.
{
"reply": [ [ "arg0", 1, 2 ] ],
"id": "cookie"
}If the call had a "type" field, then the reply will have one too containing
the DBus type signature of the arguments. If a "flags" field was present on
the call, then "flags" will also be present on the reply. Valid out flags
are:
>: Big endian message<: Little endian message
An error message is JSON object with an "error" field whose value is an
array. The array contains: error name, error arguments
{
"error": [ "org.Error", [ "Usually a message" ] ],
"id": "cookie"
}To receive signals you must subscribe to them. This is done by sending a
"add-match" message. It contains various fields to match on. If a field
is missing then it is treated as a wildcard.
"path" limits the signals to those sent by that DBus object path, it must
be a valid object path according to the specification. "path_namespace"
limits signals to the subtree of DBus object paths. It must be a valid
object path according to the specification, and may not be specified
together with "path".
"interface" limits the signals to those sent on the given DBus interface,
and must be a valid interface name. "member" limits the signal to those
of that signal name. "arg0" limits the signals to those that have the
given string as their first argument. If any of the values are not
valid according to the dbus specification, the channel will close with
a "protocol-error".
{
"add-match": {
"name": "org.the.Name",
"path": "/the/path",
"interface": "org.Interface",
"member": "SignalName",
"arg0": "first argument"
}
}If the "name" field is omitted, it will be populated from the "open" message.
If no "name" was specified in the "open" message, then DBus messages from any
bus name will be matched.
To unsubscribe from DBus signals, use the "remove-match" message. If a
match was added more than once, it must be removed the same number of
times before the signals are actually unsubscribed.
The form of "remove-match" is identical to "add-match".
{
"remove-match": {
"name": "org.the.Name",
"path": "/the/path",
"interface": "org.Interface",
"member": "SignalName",
"arg0": "first argument"
}
}Signals are sent in JSON objects that have a "signal" field, which is an
array of parameters: path, interface, signal name, and arguments. arguments
may be null if the DBus signal had no body.
{
"signal": [ "/the/path", "org.Interface", "SignalName", [ "arg0", 1, 2 ] ]
}If a signal message is sent to the bridge, the signal will be emitted.
In addition a "destination" field may be present to indicate whether
the signal should be broadcast or not.
{
"signal": [ "/the/path", "org.Interface", "SignalName", [ "arg0", 1, 2 ] ]
}If the bus name of the sender of the signal does not match the "name" field of
the "open" message, then a "name" field will be included with the "signal" message.
Properties can be watched with the "watch" request. Either a "path" or
"path_namespace" can be watched. Property changes are listened for with
DBus PropertiesChanged signals. If a "path_namespace" is watched and
path is a DBus ObjectManager, then it is used to watch for new DBus
interfaces, otherwise DBus introspection is used. The "id" field is
optional, if present a "reply" will be sent with this same "id" when
the watch has sent "notify" messages about the things being watched.
{
"watch": {
"name": "org.the.Name",
"path": "/the/path/to/watch",
"interface": "org.Interface"
},
"id": 5
}To remove a watch, pass the identical parameters with an "unwatch"
request.
{
"unwatch": {
"path": "/the/path/to/watch"
}
}If the "name" field is omitted, it will be populated from the "open" message.
Either a "name" field must be specified here or in the "open" message.
Property changes will be sent using a "notify" message. This includes
addition of interfaces without properties, which will be an empty
interface object, or interfaces removed, which will be null. Only the
changes since the last "notify" message will be sent.
{
"notify": {
"/a/path": {
"org.Interface1": {
"Prop1": "x",
"Prop2": 1
},
"org.Interface2": { }
},
"/another/path": {
"org.Removed": null
}
}
}If the bus name of the sender of the signal does not match the "name" field of
the "open" message, then a "name" field will be included with the "notify" message.
Interface introspection data is sent using "meta" message. Before the
first time an interface is sent using a "notify" message, a "meta"
will be sent with that interface introspection info. Additional fields
will be defined here, but this is it for now.
{
"meta": {
"org.Interface": {
"methods": {
"Method1": { },
"Method2": { }
},
"properties": {
"Prop1": { "flags": "rw" },
"Prop2": { "flags": "r" }
}
}
}
}If the bus name of the sender of the signal does not match the "name" field of
the "open" message, then a "name" field will be included with the "meta" message.
Such meta information can also be sent to the bridge, in order to populate the
cache of introspection data for the channel.
When the owner of the DBus "name" (specified in the open message) changes an "owner"
message is sent. The owner value will be the id of the owner or null if the name
is unowned.
{
"owner": "1:"
}A "publish" message can be used to export DBus interfaces on the bus. The bridge
will then send "call" messages back to the frontend for each method invocation
on the published interface and object path. The interface must first be described
with DBus meta information. If a cookie is specified then a reply will be sent
when the interface is published. If the interface is already published at the given
path, it will be replaced.
{
"publish": [ "/a/path", "org.Interface" ],
"id": "cookie"
}An "unpublish" message will unexport a DBus interface on the bus. It is not an
error if no such interface has been published.
{
"unpublish": [ "/a/path", "org.Interface" ]
}DBus types are encoded in various places in these messages, such as the
arguments. These types are encoded as follows:
- byte, int16, uint16, int32, uint32, int64, uint64, double: encoded
as a JSON number - boolean: encoded as a JSON boolean
- string, object-path, signature: encoded as JSON strings
- array of bytes: encoded as a base64 string
- other arrays, struct/tuple: encoded as JSON arrays
- dict: encoded as a JSON object, if the dict has string keys then those
are used as property names directly, otherwise the keys are JSON encoded and
the resulting string is used as a property name. - variant: encoded as a JSON object with a "v" field containing a value
and a "t" field containing a DBus type signature.
{
"v": "value",
"t": "s"
}Payload: http-stream2
This channel represents a single HTTP request and response. The request
HTTP headers are sent in the "open" message, and response headers are
returned in a "response" control message.
The remaining messages make up the HTTP request and response bodies.
When the channel is in text mode, the response body is automatically
coerced to UTF-8.
The following HTTP methods are not permitted:
- 'CONNECT'
The following headers are not permitted on requests, some of these
are added automatically as appropriate, and others are stripped from
the response, in particular 'Content-Length':
- 'Accept-Charset'
- 'Accept-Ranges'
- 'Connection'
- 'Content-Length'
- 'Content-MD5'
- 'Content-Range'
- 'Range'
- 'TE'
- 'Trailer'
- 'Transfer-Encoding'
- 'Upgrade'
The following are not accepted on non-binary channels:
- 'Accept-Encoding'
- 'Content-Encoding'
Additional "open" command options are needed to open a channel of this
payload type:
- "unix": Open a channel with the given unix socket.
- "port": Open a channel with the given TCP port on localhost.
- "address": Open a channel that communicates with the given
address instead of localhost. This can be an IP address or a valid
host name. To use this option you must also specify a port.
This option should be used to communicate with external
apis and not as a synonym for the host parameter. - "method": "GET", "POST", or other valid HTTP method
You may also specify these options:
- "headers": JSON object with additional request headers
- "tls": Set to a object to use an https connection.
The TLS object can have the following options:
- "authority": Certificate authority(s) to expect as signers of peer.
- "validate": Validate the peer's certificate.
The "authority" is an object with either of the following fields:
- "file": String with a file name
- "data": String with PEM encoded data
Any data to be sent should be sent via the channel, and then the channel
should be closed without a problem.
Payload: websocket-stream1
This channel payload implements a WebSocket client. The data in the
channel is the message frames.
Most of same options for http-stream2 apply here.
The response headers are send back in a "response" control message.
Payload: stream
Raw data is sent back and forth to a socket. See cockpitstream.c. The
boundaries of the messages are arbitrary, and depend on how the kernel
and socket buffer things.
If the channel is not binary, then non-UTF-8 data is forced into UTF-8
with a replacement character.
Additional "open" command options should be specified with a channel of
this payload type:
- "unix": Open a channel with the given unix socket.
- "port": Open a channel with the given TCP port on localhost.
- "address": Open a channel that communicates with the given
address instead of localhost. This can be an IP address or a valid
host name. To use this option you must also specify a port. - "batch": Batches data coming from the stream in blocks of at least this
size. This is not a guarantee. After a short timeout the data will be
sent even if the data doesn't match the batch size. Defaults to zero. - "latency": The timeout for flushing any cached data in milliseconds.
- "spawn": Spawn a process and connect standard input and standard output
to the channel. Should be an array of strings which is the process
file path and arguments.
You can't specify both "unix" and "spawn" together. When "spawn" is set the
following options can be specified:
- "directory": The directory to spawn the process in.
- "err": If "spawn" is set, and "err" is set to "out", then stderr
is included in the payload data. If "err" is set to "ignore" then, the
stderr output will be discarded. If "err" is set to "message" then it will
be included in the close message. If "pty" is set then stderr is always
included. - "environ": This is a list of additional environment variables for the new
spawned process. The variables are in the form of "NAME=VALUE". The default
environment is inherited from cockpit-bridge. - "pty": Execute the command as a terminal pty.
- "window": An object containing "rows" and "cols" properties, which set the
size of the terminal window. Values must be integers between 0 and 0xffff.
This option is only valid if "pty" is true.
For type "spawn", the ready message contains a "pid" field with the spawned
process ID.
If an "done" is sent to the bridge on this channel, then the socket and/or pipe
input is shutdown. The channel will send an "done" when the output of the socket
or pipe is done.
If "spawn" is set and the channel is closed with a "problem" code, then the
process will be sent a SIGTERM signal.
Additionally, a "options" control message may be sent in this channel
to change the "batch", "latency", and "window" options.
Payload: packet
Raw data is sent back and forth to as messages to a socket. The message
boundaries correspond exactly to the messages sent over the socket.
If the channel is not binary, then non-UTF-8 data is forced into UTF-8
with a replacement character.
Additional "open" command options should be specified with a channel of
this payload type:
- "unix": Open a channel with the given unix socket.
- "max-size": The maximum possible size of a message. Default is 64K
Supports up to 128K. Messages larger than this will be truncated.
If an "done" is sent to the bridge on this channel, then the socket
input is shutdown. The channel will send an "done" when the output of the socket
or pipe is done.
Additionally, a "options" control message may be sent in this channel
to change the "max-size" options.
Payload: fsinfo
This channel is used to report information about a given path, and the file or
a directory which may reside there, including the possibility of updates. In
the case of directories, information can also be reported about the (directly)
contained entries.
Additional "open" command options should be specified with a channel of
this payload type:
path(mandatory): a string, an absolute path name. If this ends with/
then the path must refer to a directory.attrs(mandatory): a list of strings, the requested attributes to be
reported for the file atpath, and (optionally) its sub-items. See below
for the possibilities.fnmatch: (optional, default ''): if non-empty, this is afnmatch-style
pattern. In casepath
refers to a readable directory, anentriesattribute will be reported
(described below). Use*to report all files. Can also be used to
implement search (searc*) or hide temporary files ([!.]*).watch(optional, default: false): if the channel should watch for changesfollow(default: true): if the trailing component ofpathis a symbolic
link, whether we should follow it.follow: falsemode is not supported
forwatch(but is also mostly unnecessary).
A channel is created with an associated absolute pathname (path). A path is
a string that you pass to the open() syscall. The object returned by that
call is described by the (JSON Document) value of the channel.
Strictly speaking, the path is specified to the channel as a unicode string,
but paths on the filesystem are binary strings. This mapping is handled using
the usual Python convention: filenames are stored in utf8 and encoded and
decoded with errors='surrogateescape', which allows for handling of filenames
which are not utf8.
Conceptually, the channel represents the value of a JSON object describing what
is found at the path, or the reason for an error. Each message on the channel
is a JSON Merge Patch (RFC
7396) which updates or replaces
this object.
The top-level keys in the object are:
info: the info about the reported path (described below)error: an object with information about whyinfocan't be reported.
This is currently basically an errno formatted in several possible ways:problem: a Cockpit "problem" code type of string likenot-foundornot-directorymessage: the result of strerror(). Currently English, hopefully
translated at some pointerrno: the symbolicerrnoname likeENOENTorENOTDIR
partial:trueif the data is in a "partial" state. This is used when
sending updates in multiple pieces.
The value of the info attribute is a JSON object describing the attributes of
the file. Any attribute which cannot be read (eg: due to permissions problems)
is simply not reported.
type: a string:reg,dir,lnk,chr,blk,fifo, orsockmode: an integer representing the "file permission" bits
(S_IMODE(st_mode)). As per JSON, this is transmitted as a decimal value,
but it is meant to be interpreted in octal, in the usual way.uid: an integer, the uid of the file owner (st_uid)user: a string, or an integer if the lookup failedgid: an integer, the gid of the file group (st_gid)group: a string, or an integer if the lookup failedsize: an integer, the (apparent) size of the file (st_size)mtime: a float, the mtime of the file (st_mtim)tag: a value type: the current 'transaction tag' of the file, with the
same meaning as elsewhere in this document. Ideally: this changes if the
content of the file changes. This is the same as the tag used infsread1
andfsreplace1.entries: for directories: the contents of the directory (see below)target: for symbolic links: the target of the link (a string)targets: for directories: extra information about symlink targets for
symlinks found in the directory (see below)r-ok: boolean, whether reading is allowed. (note: checking access is expensive)w-ok: boolean, whether writing is allowed. (note: checking access is expensive)x-ok: boolean, whether executing is allowed. (note: checking access is expensive)
The entries attribute is reported iff fnmatch is non-empty, and path
refers to a readable directory. It is an object where each key is the name of
a file in the directory, and each value is an object describing that file,
using the same attributes as above. entries is not reported on entries (ie:
no recursive listing). Listing entries in attrs is equivalent tofnmatch='*'.
The targets attribute is reported iff targets is listed in attrs. It
contains information that was true at some point in time about the targets of
symbolic links that were found in the current directory. An entry is added
here only in case the entry wouldn't be found in the main entries dictionary.
For example, if path is /usr/lib and fnmatch is *.so and the directory
contains the links a.so → a-impl.so, b.so → b.so.0 and c.so -> ../x/c.so
then the targets dictionary would include b.so.0 (which isn't reported in
entries because it doesn't match fnmatch) and ../x/c.so (which isn't
reported because it's in a different directory). Monitoring these entries for
changes would be expensive, so it's simply not done. Clients are expected to
use this information to flesh out details about directory entries, answering
questions like "/bin links to usr/bin, but what type of file is that?".targets is incompatible with follow: false.
If the provided path is a symbolic link, it is followed, unlessfollow: false is specified. If symbolic links are present in the directory,
they are always reported directly (ie: not followed). follow: false is not
supported in combination with watch: true but you probably don't need it:
watch mode doesn't only watch the final path component, but all components
leading up to it, including any symlink that have been followed. That means
that you can watch /etc/localtime and find out about if the link changed, or
if the contents of the file itself changed, or was deleted or replaced, or also
if /usr, /etc, or any other directories or intermediate links were changed,
deleted, or moved out of the way.
The life-cycle of the channel works like this:
When opening the channel,
readyis always immediately sent.One or more patches are sent to modify the initial value of the channel
(null). The "initial state" is reached when the value is a object
which does not have thepartialattribute set on it.If
watchmode is false thendoneis sent immediately, and the channel is
closed.Otherwise (
watch: true), the channel remains open. When any changes are
detected, they are sent as updates to the value of the channel. This may
involve transitions between reportinginfoand reportingproblem.The channel will stay open until the client closes it.
Payload: fsread1
Returns the contents of a file and its current 'transaction tag'.
The following options can be specified in the "open" control message:
- "path": The path name of the file to read.
- "max_read_size": option to limit the amount of data that is read. If the
file is larger than the given number of bytes, no data is read and the
channel is closed with problem code "too-large". The default limit is 16
MiB. The limit can be completely removed by setting it to -1.
The ready message contains a "size-hint" when the channel is opened
with the "binary" option set to "raw".
The channel will return the content of the file in one or more
messages. As with "stream", the boundaries of the messages are
arbitrary.
If the file is modified while you are reading it, the channel is
closed with a "change-conflict" problem code. If the file is
atomically replaced as with 'rename' when you are reading it, this is
not considered an error and you will get the old content with a
correct tag.
When all content has been sent or an error has occurred, the channel
will be closed. In addition to the usual "problem" field, the "close"
control message sent by the server might have the following additional
fields:
"message": A string in the current locale describing the error.
"tag": The transaction tag for the returned file content. The tag
for a non-existing file is "-".
It is not permitted to send data in an fsread1 channel. This channel
sends a "done" when all file data was sent.
Payload: fsreplace1
Replace the content of a file.
The following options can be specified in the "open" control message:
"path": The path name of the file to replace.
"size": The expected size of the file content. If set, the file is
allocated immediately, and the channel "open" request will fail if
insufficient space is available. If this option is set, it is not
possible to delete the file (ie: sending immediate EOF will result in
a 0 byte file) This option should always be provided if possible, to
avoid fragmentation, but is particularly important for large files."tag": The expected transaction tag of the file. When the actual
transaction tag of the file is different, the write will fail. If
you don't set this field, the actual tag will not be checked. To
express that you expect the file to not exist, use "-" as the tag."attrs": a JSON object containing optional file attributes to set:
user: a string, or an integer, the uid of the file owner (st_uid).
If setgroupalso has to be set.group: a string, or an integer, the gid of the file group (st_gid)
If setuseralso has to be set.mode: an integer, usually expressed in octal, but in json it is the
equivalent value in decimal.
You should write the new content to the channel as one or more
messages. To indicate the end of the content, send a "done" message.
If you don't send any content messages before sending "done", and no
"size" was given, the file will be removed. To create an empty file,
send at least one content message of length zero, or set the "size" to
0.
If "size" is given, and less data is actually sent, then the file will
be truncated down to the size of the data that was actually sent. If
more data is sent, the file will grow (subject to additional
fragmentation and potential ENOSPC errors).
When the file does not have the expected tag, the channel will be
closed with a "change-conflict" problem code.
The new content will be written to a temporary file and the old
content will be replaced with a "rename" syscall when the channel is
closed without problem code. If the channel is closed with a problem
code (by either client or server), the file will be left untouched.
If tag is given and no equivalent attrs, file owner, mode and SELinux
context are preserved (copied from the original file). Other attributes (like
ACLs) are never copied.
In addition to the usual "problem" field, the "close" control message
sent by the server might have the following additional fields:
"message": A string in the current locale describing the error.
"tag": The transaction tag of the new content.
No payload messages will be sent by this channel.
Payload: metrics1
With this payload type, you can monitor a large number of metrics,
such as all of the metrics exposed by PCP, the Performance Copilot.
In addition to monitoring live values of the metrics, you can also
access archives of previously recorded values.
You specify which metrics to monitor in the options of the "open"
command for a new channel.
One of the options is the "source" of the metrics. Many details of
how to specify metrics and how the channel will work in detail depend
on the type of the source, and are explained in specific sections
below.
The channel guarantees that each sample returned by it contains values
for all requested metrics.
The general open options are:
"source" (string): The source of the metrics:
"direct": PCP metrics from plugins that are loaded into the
Cockpit bridge directly. Use this when in doubt."pmcd": PCP metrics from the local PCP daemon.
A string starting with "/": PCP metrics from one or more
archives.The string is either the name of an archive, or the name of a
directory. When it refers to a directory, the bridge will find
all archives in that directory and merge them. The archives must
not overlap in time."pcp-archive": PCP metrics from the default pmlogger archive.
This is the same as using the name of the default pmlogger
archive directory directly, but you don't have to know where it
is.
"metrics" (array): Descriptions of the metrics to use. See below.
"instances" (array of strings, optional): When specified, only the
listed instances are included in the reported samples."omit-instances" (array of strings, optional): When specified, the
listed instances are omitted from the reported samples. Only one
of "instances" and "omit-instances" can be specified."interval" (number, optional): The sample interval in milliseconds.
Defaults to 1000."timestamp" (number, optional): The desired time of the first
sample. This is only used when accessing archives of samples.This is either the number of milliseconds since the epoch, or (when
negative) the number of milliseconds in the past.The first sample will be from a time not earlier than this
timestamp, but it might be from a much later time."limit" (number, optional): The number of samples to return. This
is only used when accessing an archive.When no "limit" is specified, all samples until the end of the
archive are delivered.
You specify the desired metrics as an array of objects, where each
object describes one metric. For example:
[
{
"name": "kernel.all.cpu.user",
"units": "millisec",
"derive": "rate"
},
...
]A metric description can contain the following fields:
"name" (string): The name of the metric. Available metrics depend
on the source."units" (string, optional): The units that values for this metric
should be delivered in. If the given metric can not be converted
into these units, the channel is closed. The format of the string
depends on the source."derive" (string, optional): Optional computation. Possible values
are "delta" and "rate". For "delta", the channel delivers the
difference between the current and the previous value for a metric.
For "rate", the channel delivers the change per millisecond of the
metric.For both "delta" and "rate", the value for a metric will be "false"
if there is no previous value to do the computation with.
Once the channel is open, it will send messages encoded as JSON. It
will send two types of message: 'meta' messages that describe the
metrics, and 'data' messages with the actual samples.
A 'meta' message applies to all following 'data' messages until the
next 'meta' message. The first message in a channel will always be a
'meta' message.
The 'meta' messages are JSON objects; the 'data' messages are JSON
arrays.
The 'meta' messages have at least the following fields:
"metrics" (array): This field provides information about the
requested metrics. The array has one object for each metric, in
the same order as the "metrics" option used when opening the
channel.For each metric, the corresponding object contains at least the
following field:"instances" (array of strings, optional): This field lists the
instances for instanced metrics. This field is not present for
non-instanced metrics."units" (string): The units of the values for this metric.
"derive" (string): The post-processing mode, as specified in the
"open" message.
"timestamp" (number): The point in time of the next 'data' message,
in milliseconds since the epoch."interval" (number): The time interval between subsequent points in
time in the 'data' messages, in milliseconds.
Depending on the source, more fields might be present in a 'meta'
message, and more fields might be present in the objects of the
"metrics" field.
The 'data' messages are nested arrays in this shape:
[ // first point in time
[
// first metric (instanced, with two instances)
[
// first instance
1234,
// second instance
5678
],
// second metric (not instanced)
789,
// third metric
543
],
// next point in time
[
// same shape again as for the first point in time
]
]Thus, a 'data' message contains data for one or more points in time
where samples have been taken. A point in time is always oneinterval later than the previous point in time, even when they are
reported in the same 'data' message.
For real time monitoring, you will generally only receive one point in
time per 'data' message, but when accessing archived data, the channel
might report multiple points in time in one message, to improve
efficiency.
For each point in time, there is an array with samples for each
metric, in the same order as the metrics option used when opening
the channel.
For non-instanced metrics, the array contains the value of the metric.
For instanced metrics, the array contains another array with samples
for each instance, in the same order as reported in the instances
field of the most recent 'meta' message.
In order to gain efficiency, 'data' messages are usually compressed.
This is done by only transmitting the differences from one point in
time to the next.
If a value for a metric or a instance is the same as at the previous
point in time, the channel transmits a null value instead.
Additionally, null values at the end of an array are suppressed by
transmitting a shorter array.
For example, say the samples for three points in time are
1: [ 21354, [ 5, 5, 5 ], 100 ]
2: [ 21354, [ 5, 15, 5 ], 100 ]
3: [ 21354, [ 5, 15, 5 ], 100 ]then the channel will send these array instead:
1: [ 21354, [ 5, 5, 5 ], 100 ]
2: [ null, [ null, 15 ] ]
3: [ null, [ ] ]This compression only happens when the last and current value belong
to the same instance of the same metric. Thus, the client does not
need to track layout changes when decompressing data messages.
Instead of a number of null, a data message can also containfalse. This indicates an error of some kind, or an unavailable
value.
PCP metric source
Cou can use "pminfo -L" to get a list of available PCP metric names
for a "direct" source, for example. If no metric of this name exists,
the channel is closed without delivering any message.
The format of the "units" member is the same as the one used by
"pminfo -d".
The metric information objects in the 'meta' messages for PCP sources
also contain these fields:
semantics(string): The semantics of this metric, one ofcounter,instant, ordiscrete.
Only numeric metrics are currently supported. Non-numeric metrics
have all their samples set to false.
Payload: session-control
Control aspects of the current session. Currently this is limited to
the session idle timeout.
When opening a channel with payload "session-control", the bridge that
handles this channel switches on its idle timeout handling. After
opening such a channel, you are then responsible for sending messages
to the bridge that inform it of user activity. If no such message is
received (while the channel is open) for the duration of the
configured session idle timeout, the bridge will exit.
If you close the channel, the bridge will stop its idle timeout
handling.
This might change in the future such that the idle timeout handling in
the bridge is always active and cannot be switched off.
The bridge is made responsible for implementing the session idle
timeout to make sure it happens even when JavaScript stops executing
or is severely throttled, maybe due to being stopped by the Browser,
the user, or because it has crashed to such a degree that it won't
handle the session timeout anymore.
JavaScript still has to explicitly switch on the session idle timeout
handling in the bridge however (by opening a "session-control"
channel). This might change in the future and the bridge might start
the session idle timeout unconditionally.
The following channel control messages will be send by the bridge:
{ "command": "ready", "timeout": N }The first message will be "ready" and it will contain the configured
session idle timeout in seconds. A timeout value of zero means that
no timeout has been configured.
{ "command": "countdown", "counter": N }The bridge will countdown the last 30 seconds of the session and will
send these messages to announce that. You should show the countdown to
the user somewhere.
{ "command": "logout" }After the countdown, the bridge will tell you to logout with this
message. You should do a clean logout. If such a logout doesn't
happen, the bridge will forcefully exit 10 seconds later.
You can send these message to the bridge:
{ "command": "active" }This indicates that the user was active. The bridge will reset its
idle timer.
Problem codes
These are problem codes for errors that cockpit-web responds to. They should
be self explanatory. It's totally not interesting to arbitrarily invent new
codes. Instead the web needs to be ready to react to these problems. When in
doubt use internal-error.
internal-errorno-cockpitunsupported-shellno-sessionaccess-deniedauthentication-failednot-foundterminatedtimeoutunknown-hostkeyno-forwarding
Doc Routing (`doc/routing.md`)
Flow Control
Cockpit's protocol passes messages on reliable, ordered underlying
transports (eg: WebSockets, HTTP responses, stdio) between multiple
peers, arranged in in a non-rooted tree graph.
Cockpit's flow control is not about replaying messages, but avoiding
flooding any peer by sending messages along any link in the graph
too rapidly.
Cockpit's flow control operates at the level of channels in the protocol
(described in doc/protocol.md). A simple blocking of any peer's message input
would block all channels, and not have the desired effect of slowing
down a certain channel which is flooding communication.
Therefore we use a windowing flow control protocol. It is based around
per-channel "ping" control messages. In the Cockpit protocol "ping"
messages in a channel are responded to by specific leaves of the graph.
They are replied to with a "pong" containing otherwise identical content.
Other "ping" messages are used as keep alives, but these are beyond the
scope of this description.
Each channel leaf that participates in flow control sends "ping"
messages with a sequence number, and waits to see the corresponding
"pong" message with the same sequence and channel number. By checking
the "ping" sequence numbers responded to, a channel can slow input and/or
transmission to wait for the opposite leaf to catch up.
Sadly this is not enough. The leaves in of Cockpit's tree often do not
consume data themselves, but pass it on to other sockets, streams, files
and so on.
Various leaves monitor their output edge queues and hold back responding
to "ping" messages if their output edge queue is too full. This is done
by use of "pressure" signals on the output queue to indicate when pressure
is high or low.
This results in flow control that achieves its intent, but does not
make guarantees. In addition only some channels and leaves implement flow
control at this point:
cockpit.js: Responds to "ping" messages indicating that a given
sequence has reached the browser.CockpitChannel: Sends sequence pings, every so often in the channel.
Is responsible for responding to channel pings, and holding back
responses to pings if a related queue has output "pressure".
Slows down an input pipe via a "pressure" signal when too many
sequences are outstanding and have not been responded to.CockpitStream: Can listen to "pressure" signals and pause reading its
input stream. Can generate "pressure" signals when its output queue
is too full.CockpitPipe: Can listen to "pressure" signals and pause reading its
input file descriptor. Can generate "pressure" signals when its
output queue is too full.CockpitWebResponse: Can generate "pressure" signals when its output
queue is too full.WebSocketConnection: Can listen to "pressure" signals and pause accepting
incoming web socket messages. Can generate "pressure" signals when its
output queue is too full.CockpitHttpStream: Uses CockpitStream and CockpitChannel together to
implement flow control on a HTTP client connection.CockpitFsRead: Listens to "pressure" signals from the CockpitChannel
to tell it to pause reading a file.
Doc Urls (`doc/urls.md`)
Cockpit URL paths
This is developer documentation about the various resource paths in Cockpit
and their characteristics. This doesn't apply to the visible URLs shown
in the main Cockpit address bar.
Cockpit URLs are based on an application. A valid application name is
either the word cockpit or a string that begins with cockpit+
for example cockpit+application-name. Each of the following URLs
are valid for any application, just replace /cockpit/ with /cockpit+application-name/
/cockpit/staticstatic files available without authentication. Files
are cached for as long as possible, and names must change when the
contents of the file changes. The exception to this is when the application
refers to a different machine. In that case the user must be authenticated
to serve those files and the cache varies on cookie./cockpit/loginauthenticates a user and sets cookie based on application
name./cockpit/$xxxxxxxxxxxxxxx/package/path/to/file.extare files which
are cached by packages for as long as possible. The checksum changes when
any of the packages on a system change. Only available after authentication./cockpit/$xxxxxxxxxxxxxxx/*/path/to/file.extare concatenated files from
packages, as seen above./cockpit/@host/package/path/to/file.extare files from packages (on
specific hosts) that are not cached. Only available after authentication./cockpit/@host/*/path/to/file.extare concatenated files from
packages (on specific hosts). They are served uncompressed./cockpit/@host/manifests.jsonincludes a summary of all the manifest
files from all the packages/cockpit/@host/manifests.jsincludes a summary of all the manifest
files from all the packages, as an AMD loadable module/cockpit/socketThe main web socket/cockpit/channel/csrftoken?queryExternal channel URLsThese are commonly used to wrap/proxy port access for Cockpit pages, as
their JavaScript cannot talk to them directly due to Cockpit's strict
Content Security Policy.See the xhr-proxy example how to wrap a TCP HTTP
port, or the gtk-broadway example for a more
complex wrapping of a stream socket.
When loading through cockpit-ws any URL that does not begin with an
application will be handled by the shell (shell/index.html by default)
using the default application cockpit.
Direct to machine urls
Cockpit-ws supports logging in directly to a remote machine, without first
authenticating on the machine that cockpit-ws is running on. A cockpit-beiboot
processes is spawned that connects via SSH to the remote machine and all
requests are proxied via that connection.
To use this feature the application name MUST begin with an = for
example /cockpit+=machine/socket will attempt to open a socket onmachine /cockpit+machine/socket will attempt to open a socket
on localhost.
When loading through cockpit-ws any URL that does not begin with an
application will be handled by the shell (shell/index.html by default)
using the default application cockpit.
In addition any url that begins with /= will attempt to load
the shell from the specified machine. For example a URL of/=machine/system will attempt to load shell/index.html
from machine using the application cockpit+machine.
Doc Extensions README (`doc/extensions/README.adoc`)
= Antora & AsciiDoctor Extensions
We need to support AsciiDoctor for generating local HTML pages as well as manpages while packaging to various distros.
But as we also publish using Antora, there might be small differences that we need to address either on the AsciiDoctor or Antora-side, or even both.
== man-inline-macro.rb
An AsciiDoctor extension that converts manpages like cockpit(1) to links man:cockpit[1] for HTML pages and boldened *cockpit(1)* for manpages.
These manpage macros do not work for the local editing of Antora within the Cockpit repo. Within the Cockpit Docs Antora repo there is an AsciiDoctor.js extension man-inline-replace.js that replaces man: links. Future improvement here could be to move the extension to the cockpit repo and fetch it from the cockpit-docs repo, but that would require more work and doesn't provide much value at the moment.
Doc Modules Guide Nav (`doc/modules/guide/nav.adoc`)
.Deployment guide
- xref::https.adoc[]
- xref::listen.adoc[]
- xref::startup.adoc[]
- xref::multi-host.adoc[]
- xref::authentication.adoc[]
- xref::sso.adoc[]
- xref::cert-authentication.adoc[]
- xref::privileges.adoc[]
.Feature Internals
- xref::feature-systemd.adoc[]
- xref::feature-journal.adoc[]
- xref::feature-networkmanager.adoc[]
- xref::feature-firewall.adoc[]
- xref::feature-storaged.adoc[]
- xref::feature-users.adoc[]
- xref::feature-realmd.adoc[]
- xref::feature-terminal.adoc[]
- xref::feature-pcp.adoc[]
- xref::feature-machines.adoc[]
- xref::feature-selinux.adoc[]
- xref::feature-tuned.adoc[]
- xref::feature-sosreport.adoc[]
- xref::feature-packagekit.adoc[]
.Developer Guide
xref::embedding.adoc[]
xref::packages.adoc[]
xref::urls.adoc[]
xref::api-base1.adoc[]
** xref::api-cockpit.adoc[]
*** xref::cockpit-cache.adoc[]
*** xref::cockpit-channels.adoc[]
*** xref::cockpit-dbus.adoc[]
*** xref::cockpit-error.adoc[]
*** xref::cockpit-file.adoc[]
*** xref::cockpit-http.adoc[]
*** xref::cockpit-locale.adoc[]
*** xref::cockpit-location.adoc[]
*** xref::cockpit-manifest.adoc[]
*** xref::cockpit-metrics.adoc[]
*** xref::cockpit-series-data.adoc[]
*** xref::cockpit-login.adoc[]
*** xref::cockpit-spawn.adoc[]
*** xref::cockpit-util.adoc[]xref::api-shell.adoc[]
xref::api-system.adoc[]
Doc Modules Man Nav (`doc/modules/man/nav.adoc`)
.Manual pages
- xref:man:cockpit.1.adoc[]
- xref:man:cockpit.conf.5.adoc[]
- xref:man:cockpit-ws.8.adoc[]
- xref:man:cockpit-tls.8.adoc[]
- xref:man:cockpit-desktop.1.adoc[]
- xref:man:cockpit-bridge.1.adoc[]
- xref:man:pam_ssh_add.8.adoc[]
Doc Modules ROOT Attachments Schema.Json (`doc/modules/ROOT/attachments/schema.json`)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cockpit-project.org/manifest-1/schema",
"title": "Cockpit manifest file validation for Cockpit pages.",
"description": "Validates Cockpit manifest files according to the standard that is parsed and set by Cockpit itself. This is used for Cockpit pages only and does not validate any other AppStream manifest types.",
"examples": [
{
"url": "https://github.com/cockpit-project/cockpit/blob/0e3b78e0a7c841e7608c87e87821247e308d3d14/pkg/packagekit/manifest.json"
}
],
"type": "object",
"properties": {
"content-security-policy": {
"description": "By default Cockpit serves packages using a strict Content Security Policy, which among other things does not allow inline styles or scripts. This can be overridden on a per-package basis, with this setting. \n\nIf the overridden content security policy does not contain a default-src, connect-src, base-uri, form-action, object-src, or block-all-mixed-content then these will be added to the policy from the manifest.",
"type": "string"
},
"name": {
"description": "An optional string that changes the name of the package. Normally packages derive their name from the directory that they are located in. This field overrides that name.",
"type": "string",
"pattern": "^[A-Za-z0-9_]+$"
},
"priority": {
"description": "An optional integer that specifies which package is preferred in cases where there are conflicts. For example given two packages with the same name a package is chosen based on its priority. A higher integer means higher priority.",
"type": "integer"
},
"conditions": {
"description": "An optional list of {\"predicate\": \"value\"} objects. Cockpit will only consider the package if all conditions are met. Currently supported predicates are path-exists and path-not-exists. Unknown predicates are ignored. This is preferable to using priority, but only available since Cockpit 286.",
"$ref": "#/$defs/condition-types"
},
"requires": {
"description": "An optional JSON object that contains a "cockpit" string version number. The package will only be usable if the Cockpit bridge and javascript base are equal or newer than the given version number.",
"type": "object",
"required": [ "cockpit" ],
"properties": {
"cockpit": {
"type": "string",
"pattern": "^\d[\d|\.]*$"
}
},
"additionalProperties": {
"type": "string"
}
},
"version": {
"description": "An informational version number for the package.",
"type": [
"string",
"number"
]
},
"preload": {
"description": "A list of identifiers of the components that should be preloaded. Normally, the files of a component are loaded when the user navigates to it for the first time. The files of a preloaded component are loaded immediately after the user logs in, and the initialization code of the component is invoked. \n\nThe value of this field is an array of strings, where each string is one of the keys used in the dashboard, menu, or tools fields",
"type": "array",
"items": {
"type": "string"
}
},
"parent": {
"description": "Used when module does not have its own menu item but is a part of a different module.",
"type": "object",
"properties": {
"component": {
"description": "The name of the superordinate component.",
"type": "string"
},
"docs": {
"$ref": "#/$defs/docs"
}
}
},
"dashboard": {
"description": "Dashboard items appear in the menu under the section Apps.",
"$ref": "#/$defs/menu-type"
},
"menu": {
"description": "These items appear in the menu under the section System. This section is roughly ordered into these categories (with their order in parentheses):\n- System Information (10) \n- Logs (20) \n- Configuring major subsystems (30-40) \n- Things running on the machine (VMs, Containers - 50-60) \n- Implementation Details (Accounts, Services - 70-100).",
"$ref": "#/$defs/menu-type"
},
"tools": {
"description": "These items appear in the menu under the section Tools.",
"$ref": "#/$defs/menu-type"
},
"bridges": {
"description": "Details for additional server-side bridges. Each item specifies how to match requests and which executable to use.",
"type": "array",
"items": {
"$comment": "A privileged bridge can not have a 'match' property.",
"type": "object",
"if": {
"required": [ "privileged" ],
"properties": {
"privileged": {
"const": true
}
}
},
"then": {
"properties": {
"match": false
}
},
"required": [ "spawn" ],
"properties": {
"environ": {
"description": "Optional, an array of environment variables to pass to the bridge command. Dynamic values can be used with ${} syntax.",
"type": "array",
"items": {
"type": "string"
}
},
"match": {
"type": "object",
"required": [ "payload" ],
"properties": {
"payload": {
"description": "The payload pattern to match incoming requests.",
"type": "string"
}
}
},
"privileged": {
"description": "If set to true, marks the bridge as a superuser bridge, to be started with escalated privileges. A privileged bridge can not have a "match" property.",
"default": false,
"type": "boolean"
},
"label": {
"description": "Optional, sets a label on the bridge for selection in the UI, useful for privileged bridges.",
"type": "string"
},
"problem": {
"description": "If specified, when bridge fails to start it will close the associated channel with this problem code.",
"type": "string"
},
"spawn": {
"description": "The command and arguments to invoke the bridge. Dynamic values can be used with ${} syntax.",
"type": "array"
}
}
}
}
},
"$defs": {
"docs": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "List of documentation URLs for the given page, describing where the component is located within Cockpit.",
"type": "array",
"items": {
"type": "object",
"required": [ "label", "url" ],
"properties": {
"label": {
"description": "The label for the documentation page.",
"type": "string"
},
"url": {
"description": "The URL for the documentation page.",
"type": "string"
}
}
}
},
"menu-type": {
"type": "object",
"properties": {
"index": {
"description": "An index of menu items with labels and their order.",
"type": "object",
"properties": {
"label": {
"description": "The displayed text of the menu item.",
"type": "string"
},
"order": {
"description": "An optional order integer to place this menu item or tool. Lower integers are listed first.",
"type": "integer"
},
"path": {
"description": "The relative path to the HTML file within the package that implements the menu item or tool.",
"type": "string"
},
"docs": {
"$ref": "#/$defs/docs"
},
"keywords": {
"description": "Keywords that describe the page and which are used for searching. These keywords should be lowercase. Page label is prepended as first keyword in the first keyword item.",
"type": "array",
"items": {
"type": "object",
"properties": {
"matches": {
"description": "An array of strings matching keywords in lowercase.",
"type": "array",
"items": {
"type": "string",
"pattern": "^[^A-Z]+$"
}
},
"goto": {
"description": "Optional path that is used for all keywords in this item. When this argument starts with slash, then it is used as pathname, otherwise it is used as hash. Defining goto:\"page_hash\" in page with path:\"/page_path\" would redirect to /page_path#page_hash, while goto:\"/page_path\" would redirect to /page_path ignoring default page path.",
"type": "string"
},
"weight": {
"description": "How much keywords are prioritized over others.",
"default": 3,
"type": "integer"
},
"translate": {
"description": "If keywords should be localized or not",
"default": true,
"type": "boolean"
}
}
}
}
}
}
}
},
"predicates": {
"type": "object",
"additionalProperties": false,
"properties": {
"path-exists": {
"description": "A condition where a specific file path must exist.",
"type": "string"
},
"path-not-exists": {
"description": "A condition where a specific file path must not exist.",
"type": "string"
},
"any": {
"description": "A list of conditions that evaluate true if at least one of the predicates is met.",
"type": "array",
"items": {
"$ref": "#/$defs/predicates"
}
}
}
},
"condition-types": {
"type": "array",
"items": {
"$ref": "#/$defs/predicates"
}
}
}
}
Doc Modules Guide Bundling Cockpit Guide (`doc/modules/guide/bundling/cockpit-guide.adoc`)
= Cockpit documentation
:leveloffset: +1
include::../pages/index.adoc[]
:leveloffset: +1
include::../pages/cockpit-manual.adoc[]
include::../pages/https.adoc[]
include::../pages/listen.adoc[]
include::../pages/startup.adoc[]
include::../pages/multi-host.adoc[]
include::../pages/authentication.adoc[]
include::../pages/sso.adoc[]
include::../pages/cert-authentication.adoc[]
include::../pages/privileges.adoc[]
:leveloffset: -1
include::../pages/features.adoc[]
include::../pages/development.adoc[]
:leveloffset: -1
Doc Modules Guide Pages Api Base1 (`doc/modules/guide/pages/api-base1.adoc`)
= API: base1
This package contains basic support API available to other packages.
Doc Modules Guide Pages Api Cockpit (`doc/modules/guide/pages/api-cockpit.adoc`)
= cockpit.js
Basic cockpit API to interact with the system
[[api-cockpit-loading]]
== Loading cockpit.js
+cockpit.js+ should be loaded via a script tag.
....
+` into your page to registerthe manifests at the +cockpit.manifests+ global variable.
You can also load +../manifests.json+ directly in your page, with
https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch[`+fetch()+`].
Doc Modules Guide Pages Cockpit Manual (`doc/modules/guide/pages/cockpit-manual.adoc`)
[[cockpit-manual]]
= Manual pages
:leveloffset: +1
:man: ../../man/pages
include::{man}/cockpit.1.adoc[]
include::{man}/cockpit.conf.5.adoc[]
include::{man}/cockpit-ws.8.adoc[]
include::{man}/cockpit-tls.8.adoc[]
include::{man}/cockpit-desktop.1.adoc[]
include::{man}/cockpit-bridge.1.adoc[]
include::{man}/pam_ssh_add.8.adoc[]
:leveloffset: -1
Doc Modules Guide Pages Cockpit Metrics (`doc/modules/guide/pages/cockpit-metrics.adoc`)
= cockpit.js: Metrics
Metrics about the system can be retrieved from several sources using
link:#cockpit-metrics-function[+cockpit.metrics()+] metrics channels. The
metrics are made available as series data, and can be used with the
xref:cockpit-series-data.adoc#cockpit-series[+cockpit.series()+] and
xref:cockpit-series-data.adoc#cockpit-grid[+cockpit.grid()+] facilities.
[[cockpit-metrics-function]]
== cockpit.metrics()
....
metrics = cockpit.metrics(interval, options, cache)
....
Opens a new metrics channel. The data retrieved will be available in the
link:#cockpit-metrics-series[+metrics.series+] series sink, and can be
used together with xref:cockpit-series-data.adoc#cockpit-grid[+cockpit.grid()+] objects.
The +interval+ is in milliseconds, and is the granularity of the
series data retrieved. Any grids consuming the data must have the same
interval.
The +cache+ argument is a cache identifier. If specified, then this
metrics channel will share data with other metrics channels of the same
identifier. Make sure to use a globally unique string.
The +options+ argument is either a javascript plain object, or an
array of those. Each object can have the following fields.
+"metrics"+::
An array of full metric descriptions, as javascript objects. The
specifics of these, and how to determine which ones to use, can
unfortunately only be found in the low-level protocol documentation.
This option is required.+"source"+::
The source to use for real-time data. This is used by the +follow+
method, see below. Set to +"internal"+ to retrieve internal metrics
read by the bridge. If set to +"direct"+ or +"pmcd"+ then data
will be retrieved from https://pcp.io[PCP]if it is available. The
default is +"internal"+.+"archive_source"+::
The source to use for retrieving historical data. This is used by the
+fetch+ method, see below. Set to +"pcp-archive"+ to retrieve data
from PCP archives. The default is not to try to retrieve historical
data.
When the +options+ argument is an array of javascript objects, then
the metrics channel tries to use them in order until one succeeds. This
way, you can prefer PCP as the source but fall back to internal metrics
when PCP is not available, for example. The channel gives no indication
which of the options has been used, and +fetch+ and +follow+ might
use different entries from the list.
[[cockpit-metrics-fetch]]
== metrics.fetch()
....
metrics.fetch(beg, end)
....
Retrieve archived metrics data between +beg+ and +end+. The
arguments can either be numbers, in which case they are interval based
offsets, or they can be javascript Date objects.
[[cockpit-metrics-follow]]
== metrics.follow()
....
metrics.follow()
....
Start retrieving live metrics data as it become available.
[[cockpit-metrics-close]]
== metrics.close()
....
metrics.close()
....
Stop the retrieval of metrics and release resources.
[[cockpit-metrics-series]]
== metrics.series
The series sink where data retrieved data will be processed.
[[cockpit-metrics-meta]]
== metrics.meta
The metrics meta data last received.
[[cockpit-metrics-onchanged]]
== metrics.onchanged
....
metrics.onchanged = function() { }
....
An event triggered when one of the properties on this metrics object
changes.
Doc Modules Guide Pages Cockpit Series Data (`doc/modules/guide/pages/cockpit-series-data.adoc`)
= cockpit.js: Series Data
Series data consists of values along a continuous (usually time) axis.
We can place these in grids which expose a distinct subset of these
values. These are the underlying mechanism for displaying metrics data
in graphs.
[[cockpit-grid]]
== cockpit.grid()
....
grid = cockpit.grid(interval, [beg, end])
....
Creates a grid object to contain series data.
The +interval+ is the granularity of the grid. Usually this is a
number of milliseconds, when used with time series data. The +beg+ and+end+ are the bounds of the grid. If omitted they will be set to zero
for an initially empty grid.
If +beg+ and/or +end+ are negative (including negative zero) then
they are interpreted in number of intervals relative to the current
time. Thus cockpit.grid(1000, -300, -0) will create a grid for the most
recent 5 minutes.
[[cockpit-grid-add]]
== grid.add()
....
row = grid.add(series, path)
row = grid.add(callback, [early])
row = grid.add()
....
Adds a row to the grid. The returned +row+ is a Javascript array that
will contain series data. The arguments control how the row is populated
from the series data. The +row+ is a sparse array. Its +row.length+
will not match the expected size of the grid, unless and until the row
has been completely filled in. The first index of the +row+ will
contain the data from the series data at the
link:#cockpit-grid-beg[+grid.beg+] offset.
When no arguments are passed, an empty row is added, and it is not
populated with data.
When called with a +series+ and +path+ argument then the row will be
populated directly with series data. The +series+ can either be a
link:#cockpit-series[series object] or an object that has an+obj.series+ property. The link:#cockpit-series-interval[series
interval] must match the interval of this grid. If +path+ is missing
or empty, then the series data is placed into the row directly.
Otherwise +path+ indicates which part of the series data to place in
the row. When +path+ is an array, it is used as a set of property
names or array indexes to follow into nested series data. When +path+
is a dotted string, it is split and used the same way to locate the
correct value in nested series data. The exact format of the series data
depends on its producer, and relevant paths will be documented there.
If a +callback+ function is specified, then it will be invoked to
provide series data for the row. The function is invoked as+callback(row, index, count)+, where the +row+ is the row to fill
in, the +index+ is the index to start filling in and +count+ is the
number of items to fill in. The +this+ variable will be set to the
grid while invoking the +callback+. The callback is called after other
data rows for a given series have been filled in. Callbacks are called
in the order added, unless the +early+ argument is set to +true+, in
which case the callback is called earlier than callbacks without the+early+ argument set.
To remove the row use the link:#cockpit-grid-remove[+grid.remove()+
method.]
The row will start being populated with data when the +series+
produces data. To make this happen right away, use the
link:#cockpit-grid-sync[+grid.sync()+] method.
[[cockpit-grid-remove]]
== grid.remove()
....
grid.remove(row)
....
Remove a previously added +row+ from the grid. The row will no longer
be updated with series data.
[[cockpit-grid-sync]]
== grid.sync()
....
grid.sync()
....
Load or reload data from the series into the rows. This does not clear
the rows before populating them. Some data may be populated immediately,
others may have to wait until data can be loaded. Internally this
function calls link:#cockpit-series-load[+series.load()+] for each
series.
All rows with callbacks will be invoked to regenerate all the data. The
link:#cockpit-grid-onnotify[+grid.onnotify+] event will be triggered.
It is not necessary to call this function after a call of the
link:#cockpit-grid-move[+grid.move()+] method.
[[cockpit-grid-move]]
== grid.move()
....
grid.move(beg[, end])
....
Move the grid to new +beg+ and +end+ range. Data will be discarded
from the rows and link:#cockpit-grid-sync[+grid.sync()+] will be
called to load or reload series data for the new range of offsets.
If +end+ is not specified it will be set to +beg+. If +beg+ and/or+end+ are negative (including negative zero) then they will be set to
the number of intervals prior to the current time taken as an interval.
If +beg+ and/or +end+ are negative (including negative zero) then
they are interpreted in number of intervals relative to the current
time. Thus cockpit.grid(1000, -300, -0) will create a grid for the most
recent 5 minutes.
[[cockpit-grid-walk]]
== grid.walk()
....
grid.walk()
....
Move the grid forward every
link:#cockpit-grid-interval[+grid.interval+] milliseconds. To stop
moving forward, call link:#cockpit-grid-move[+grid.move()+].
[[cockpit-grid-notify]]
== grid.notify()
....
grid.notify(index, count)
....
This function is called to have rows with callbacks recalculate their
data. It is not normally necessary to call this function, as it will be
invoked automatically when new series data is available or has been
loaded. This function triggers the
link:#cockpit-grid-onnotify[+grid.onnotify+] event.
[[cockpit-grid-onnotify]]
== grid.onnotify
....
grid.addEventListener("notify", function(index, count) { ... });
....
An event that is triggered when some part of the series data in grid
changes. The +index+ is the row index where things changed, and the+count+ is the length of the data that changed.
[[cockpit-grid-close]]
== grid.close()
....
grid.close()
....
Close the grid, and stop updating the rows.
[[cockpit-grid-interval]]
== grid.interval
The granularity of the grid. For time series data this is an interval in
milliseconds. In order to use a given link:#cockpit-grid[grid] and
link:#cockpit-series[series] together, their interval properties must
match.
[[cockpit-grid-beg]]
== grid.beg
The beginning offset of the series data in the grid. Do not set this
property directly. Use the link:#cockpit-grid-move[grid.move()] method
instead.
[[cockpit-grid-end]]
== grid.end
The ending offset of the series data in the grid. Do not set this
property directly. Use the link:#cockpit-grid-move[grid.move()] method
instead.
[[cockpit-series]]
== cockpit.series()
....
series = cockpit.series(interval, [cache, fetch])
....
Create a new sink of series data. This is usually done by producers of
series data, and it is rare to invoke this function directly.
The +interval+ is the granularity of the series data. For time series
data this is an interval in milliseconds. If a +cache+ string is
specified, series data will be cached across frames for series with the
same +cache+ cache identifier to load and/or reload.
If a +fetch+ callback is specified, then it will be invoked when grids
request certain ranges of data. The +fetch+ callback is invoked with+function fetch(beg, end) { ... }+ range offsets. The
link:#cockpit-series-input[series.input()] should be called with data
retrieved, either immediately or at a later time. The callback may be
called multiple times for the same ranges of data. It is up to the
callback to determine when or whether it should retrieve the data more
than once.
A producer of series data, usually calls this function and creates
itself a +obj.series+ property containing this series object.
[[cockpit-series-input]]
== series.input()
....
series.input(beg, items[, mapping])
....
Send series data into the series sink. Any grids that have added rows
based on this series, will have data filled in. The +beg+ is the
beginning offset of +items+. The +items+ are an array one or more
series data items.
Producers may wish to provide additional properties that can be used in
lookup paths that rows can pull from. This is done in the +mapping+
argument. If specified it is a tree of objects. Each sub object should
have a property with the name +""+ empty string, which will be used as
the property name or index in place of the one used in the lookup path.
[[cockpit-series-load]]
== series.load()
....
series.load(beg, end)
....
Load data from the series into any grids that have rows based on this
series data. Any cached data will be filled in immediately. Any data not
cached, will be requested from the producer, if possible, and may arrive
at a later time.
The +beg+ and +end+ denote the range of data to load.
[[cockpit-series-interval]]
== series.interval
The granularity of the series. For time series data this is an interval
in milliseconds. In order to use a given link:#cockpit-grid[grid] and
link:#cockpit-series[series] together, their interval properties must
match.
[[cockpit-series-limit]]
== series.limit
The maximum number of items to cache for loading and/or reloading. You
can change this value to a different number. Having a number close to
zero will break certain usage of grids, such as
link:#cockpit-grid-walk[+grid.walk()+].
Doc Modules Guide Pages Cockpit Spawn (`doc/modules/guide/pages/cockpit-spawn.adoc`)
= cockpit.js: Spawning Processes
This is the API for spawning a process and receiving its output, as well
as exit codes.
[[cockpit-spawn-spawn]]
== cockpit.spawn()
....
process = cockpit.spawn(args, [options])
....
Spawns a process on the system.
The +args+ should be an array starting with the executable and
containing all the arguments to pass on the command line. If +args+ is
a string then it is interpreted as an executable name. The optional+options+ argument is a javascript plain object and can contain any of
the following fields:
+"binary"+::
If set to +true+ then handle the input and output of the process as
arrays of binary bytes.+"directory"+::
The directory to spawn the process in.+"err"+::
Controls where the standard error is sent. By default it is logged to
the journal. If set to +"out"+ it is included in with the output
data. If set to +"ignore"+ then the error output is discarded. If
set to +"message"+, then it will be returned as the error message.
When the +"pty"+ field is set, this field has no effect.+"environ"+::
An optional array that contains strings to be used as additional
environment variables for the new process. These are +"NAME=VALUE"+
strings.+"pty"+::
Launch the process in its own PTY terminal, and send/receive terminal
input and output.+"batch"+::
Batch data coming from the process in blocks of at least this size.
This is not a guarantee. After a short timeout the data will be sent
even if the data doesn't match the batch size. Defaults to zero.+"latency"+::
The timeout for flushing any cached data in milliseconds.+"superuser"+::
Set to +"require"+ to spawn the process as root instead of the
logged in user. If the currently logged in user is not permitted to
become root (eg: via +pkexec+) then the +client+ will immediately
be xref:cockpit-dbus.adoc#cockpit-dbus-onclose[closed] with a +"access-denied"+
problem code.
+
Set to +"try"+ to try to run the process as root, but if that fails,
fall back to an unprivileged process.
The spawned process is a
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise[promise]
that will complete if the process exits successfully, or fail if there's
a problem. Some additional methods besides the standard promise methods
are documented below.
The standard output of the process is made available via the spawned
process object. Any non-UTF8 output from the process will be coerced
into textual form. It is highly recommended that only textual output be
produced by the command. The standard error is logged to the journal.
[[cockpit-spawn-script]]
== cockpit.script()
....
process = cockpit.script(script, [args], [options])
....
Run a shell script on the system.
This function link:#cockpit-spawn-spawn[spawns] a Bourne shell script
process. The full text of the +/bin/sh+ shell script should be passed
in as the first argument. The +args+ can be an array of arguments, not
including the executable, which are passed to the script as +$1+,+$2+ and so on. Shebang options are not used or respected.
The +options+ is an optional javascript plain object and can include
any of the fields listed for the
link:#cockpit-spawn-spawn[+cockpit.spawn()+] function.
The spawned process is a
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise[promise]
that will complete if the script exits successfully, or fail if there's
a problem. Some additional methods besides the standard promise methods
are documented below.
The standard output of the process is made available via the spawned
process object. Any non-UTF8 output from the process will be coerced
into textual form. It is highly recommended that only textual output be
produced by the command. The standard error is logged to the journal by
default.
[[cockpit-spawn-then]]
== process.then()
....
process.then((data[, message]) => { ... })
....
This is a standard
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise[promise]
method. It sets up a handler to be called when the process finishes
successfully.
The +data+ argument contains the standard output of the process. If it
a string, unless the process was opened in binary mode, in which case
the +data+ is an array of bytes. If a +process.stream()+ handler is
set up, then any standard output data consumed by the handler will not
be included in the +data+ argument.
If the process was spawned with the +"err"+ option set to+"message"+ then the second argument will contain the standard error
output of the process.
[[cockpit-spawn-catch]]
== process.catch()
....
process.catch((exception[, data]) => { ... })
....
This is a standard
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise[Promise]
method. It sets up a handler to be called when the process fails,
terminates or exits.
The +exception+ object passed to the handler can have the following
fields:
+message+::
A message describing the exception. If the process was spawned with
the +"err"+ option set to +"message"+ then the second argument
will contain the standard error output of the process.+problem+::
A xref:cockpit-error.adoc#cockpit-problems[problem code] string when a problem occurred
starting or communicating with the process. This is +null+ if the
process exited or was terminated.+exit_status+::
The numeric exit status of the process. This is +null+ if the
process did not exit.+exit_signal+::
A string representing a unix signal that caused the process to
terminate. This is +null+ if the process did not terminate because
of a signal.
If the process actually ran and produced output before failing, it will
be available in the +data+ argument. Otherwise this argument will be+undefined+.
[[cockpit-spawn-stream]]
== process.stream()
....
process.stream(data => { ... })
....
This sets up a handler to be called when the process has standard
output. The handler will be called multiple times. The handler will be
called regardless of whether the process ends up exiting successfully or
not.
Only one handler may be registered at a time. Registering an additional
handler replaces the previous one. The handler receives either string+data+ or an array of binary bytes as its argument. A stream handler
may return a number, which indicates the number of characters or bytes
consumed from +data+. Any data not consumed will be included again the
next time the handler is called.
If a +process.stream()+ handler is set up, then the +process.then()+
handlers will only get any remaining data not consumed by the stream
handler.
[[cockpit-spawn-input]]
== process.input()
....
process.input(data, [stream])
....
This method writes +data+ to the standard input of the process. If+data+ is +null+ or +undefined+ it is not sent. The +data+
should be a string or an array of bytes if the process was opened in
binary mode.
If +stream+ is set to +true+ then this function may be called again
with further input. Otherwise the standard input of the process is
closed.
[[cockpit-spawn-close]]
== process.close()
....
process.close([problem])
....
Close the process by closing its standard input and output. If+problem+ is specified it should be a standard
xref:cockpit-error.adoc#cockpit-problems[problem code] string. In this case the process
will be terminated with a signal.
Doc Modules Guide Pages Cockpit Util (`doc/modules/guide/pages/cockpit-util.adoc`)
= cockpit.js: Utilities
Various utility functions
[[cockpit-format]]
== cockpit.format()
....
string = cockpit.format(template, args)
string = cockpit.format(template, [arg, ...])
....
Format a string interpolating +args+ into +template+ using shell
like syntax. The +args+ may be either an array or javascript object.
The +template+ can contain fields that look like +$name+ or+${name}+ or +$0+. Numeric fields are used with array +args+ and
start at zero.
In the second form, multiple +arg+ arguments may be passed directly,
and interpolated as as numeric fields in the +template+.
All falsy arguments except the numbers +0+ and +0.0+are replaced by
an empty string.
[[cockpit-format-number]]
== cockpit.format_number()
....
string = cockpit.format_number(number, [precision])
....
Formats +number+ into a displayable +string+. If the number is not
an integer, it is rounded to the given number of decimal places,
defaulting to 3. If the number is near zero, but not quite zero it is
rounded to the smallest non-zero value of the given precision; i.e.
±0.001 for default precision 3.
If +number+ is +null+ or +undefined+ an empty string will be
returned.
[[cockpit-format-bytes]]
== cockpit.format_bytes()
....
string = cockpit.format_bytes(number, [options])
....
Formats +number+ into a displayable +string+ with a suffix, such as
kB or MB.
By default, SI units are used. IEC units (1024-based) can be requested
by including +base2: true+ in +options+.
By default, non-integer numbers will be formatted with 3 digits of
precision. This can be changed with +options.precision+.
If +number+ is +null+ or +undefined+ an empty string will be
returned.
[[cockpit-format-bytes-per-sec]]
== cockpit.format_bytes_per_sec()
....
string = cockpit.format_bytes_per_sec(number, [options])
....
Format +number+ of bytes into a displayable speed +string+.
This function is mostly equivalent to +cockpit.format_bytes()+ but the
returned value contains a unit like kB/s or MB/s.
[[cockpit-format-bits-per-sec]]
== cockpit.format_bits_per_sec()
....
string = cockpit.format_bits_per_sec(number, [options])
....
Format +number+ of bits into a displayable speed +string+.
This function is mostly equivalent to +cockpit.format_bytes()+ but the
returned value contains a unit like kbps or Mbps.
This function does not support IEC units. +base2+ may not be passed as
part of +options+.
[[cockpit-init]]
== cockpit.init()
....
await cockpit.init();
cockpit.init().then(() => { ... });
....
Requests initialization of the Cockpit client library. This will ensure
that the transport is connected and we are ready to create channels. It
also populates the +cockpit.info+ field.
This function returns a promise. Initialization isn't complete until the
promise has resolved. You can either +await+ it or call +.then()+ on
it.
[[cockpit-info]]
== cockpit.info
....
cockpit.info.channels[payload]
cockpit.info.os_release[field]
cockpit.info.user
cockpit.info.ws.version
....
This object contains information about Cockpit itself. It is only
available after cockpit.init() has been called and awaited.
+channels+::
This is a mapping of channel payload types (keys, strings) supported
by the bridge to capabilities advertised by those channels (values,
lists of strings). Channels are listed even if they don't advertise
any capabilities, making this useful as a way to determine which
channel types are supported by the bridge.+os_release+::
This is the data from the +/etc/os-release+ or
+/usr/lib/os-release+ on the system that the bridge is running on.
It is a mapping from the key names to their values. See the
https://www.freedesktop.org/software/systemd/man/latest/os-release.html[os-release
Specification] for information about the available keys.+user+::
Contains information about the user we're logged in as.
+uid+;;
This is unix user id as an integer.
+gid+;;
This is unix user group id as an integer.
+name+;;
This is the unix user name like +"root"+.
+fullname+;;
This is a readable name for the user, from the GECOS field.
+group+;;
This is the primary group name of the user.
+groups+;;
This is an array of group names to which the user belongs. The first
item in this list is the primary group.
+home+;;
This is user's home directory.
+shell+;;
This is unix user shell.+ws+::
Contains information about the webserver Cockpit is being served with.
+version+;;
The version of the webserver.
[[cockpit-event-target]]
== cockpit.event_target
....
cockpit.event_target(object, [handlers])
....
Adds an
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget[EventTarget]
implementation to the +object+. Optionally store the handlers in+handlers+ if its specified.
Doc Modules Guide Pages Development (`doc/modules/guide/pages/development.adoc`)
[[development]]
= Developer Guide
:leveloffset: +1
include::embedding.adoc[]
include::packages.adoc[]
include::urls.adoc[]
include::api-base1.adoc[]
:leveloffset: +1
include::api-cockpit.adoc[]
include::cockpit-cache.adoc[]
include::cockpit-channels.adoc[]
include::cockpit-dbus.adoc[]
include::cockpit-error.adoc[]
include::cockpit-file.adoc[]
include::cockpit-http.adoc[]
include::cockpit-locale.adoc[]
include::cockpit-location.adoc[]
include::cockpit-login.adoc[]
include::cockpit-manifest.adoc[]
include::cockpit-metrics.adoc[]
include::cockpit-series-data.adoc[]
include::cockpit-spawn.adoc[]
include::cockpit-util.adoc[]
:leveloffset: -1
include::api-shell.adoc[]
include::api-system.adoc[]
:leveloffset: -1
Doc Modules Guide Pages Embedding (`doc/modules/guide/pages/embedding.adoc`)
[[embedding]]
= Embedding and Integrating Cockpit
Cockpit can be embedded in other web applications either as a whole or
specific Cockpit components can be integrated. Due to frame security
policy restrictions, this only works if Cockpit and the web application
have the same origin; this is commonly achieved by running both from a
common reverse proxy.
[[embedding-full]]
== Embedding the Cockpit Interface
Cockpit can be embedded into a larger web page as a frame. To embed the
entire Cockpit Window use the URI: +https://server.example.com:9090/+
[source,html]
This is Cockpit.[[embedding-components]]
== Integrating Cockpit Components into Web Applications
Instead of embedding the entirety of Cockpit, you can integrate specific
components. Only those components explicitly documented as API should be
integrated. Other components can and will change regularly.
The component will load from the server in question and a WebSocket
connection will be established with the server to relay the component's
message stream.
Cockpit components are HTML files contained in xref:packages.adoc[packages].
These can be placed in an iframe or web browser window. Each documented
and stable component has a well-known URL and these are documented in
the xref:development.adoc[API reference]. Each component URL begins with the
string +/cockpit/@localhost/+ followed a package name, and then the
component itself.
For example the xref:api-terminal-html.adoc[terminal.html] in the
xref:api-system.adoc[system] package, has this URL:+/cockpit/@localhost/system/terminal.html+
[source,html]
This is a terminal.[[embedding-deep]]
== Deep Integration
Most often link:#embedding-components[simple integration] will be used
to bring Cockpit components into web applications. However it is also
possible to do deep integration for embedders who wish to perform
non-standard authentication with the server, and relay the component's
message stream to the server themselves.
[WARNING]
Deep integration capability is in heavy flux and is not yet documented.
[[embedding-cors]]
== Pinging Cockpit
When embedding Cockpit or integrating Cockpit components, it may be
necessary to check whether Cockpit is available on a server before
proceeding.
To do this perform a +/ping+ request to Cockpit. This is a simple HTTP
GET request. It returns the following:
....
GET: https://server.example.com:9090/ping
200 OK: { "service": "cockpit" }
....
The +/ping+ request allows
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross Origin
Resource Sharing] headers and as such can be performed from Javascript
code with any origin. The request can also be made via plain HTTP
without SSL. It is by design that no further information is present in
the response.
A complete example of using +/ping+ is available in the Cockpit
sources in the +/examples/ping-server/+ directory.
Doc Modules Guide Pages Feature Firewall (`doc/modules/guide/pages/feature-firewall.adoc`)
[[feature-firewall]]
= Firewall
Cockpit uses https://www.firewalld.org[firewalld] to interact with the
system's firewall. No firewall configuration UI will be shown if
firewalld is not installed.
Firewalld controls access to its APIs via PolicyKit. The user logged
into Cockpit needs to have the appropriate permissions to view or modify
the settings.
Cockpit can currently only show, add, and remove predefined firewalld
services in the default zone.
To perform similar tasks from the command line, use
https://www.firewalld.org/documentation/man-pages/firewall-cmd.html[firewall-cmd].
For example, to get the same list of allowed services that Cockpit
displays:
....
$ sudo firewall-cmd --list-services
dhcpv6-client samba-client mdns ssh cockpit
....
To enable an additional service, use:
....
$ firewall-cmd --add-service pop3
success
....
Doc Modules Guide Pages Feature Journal (`doc/modules/guide/pages/feature-journal.adoc`)
[[feature-journal]]
= Journal
The systemd journal provides Cockpit with indexed log data. This log
data is found on the Journal page, as well as in various other places
when configuring services, storage, networking etc.
Cockpit accesses Journal data via the
https://www.freedesktop.org/software/systemd/man/journalctl.html[`+journalctl+`]
command. Similar tasks can be performed at the command line:
....
$ sudo journalctl -f -u docker
...
....
Doc Modules Guide Pages Feature Machines (`doc/modules/guide/pages/feature-machines.adoc`)
[[feature-machines]]
= Multiple Machines
[WARNING]
This feature is deprecated as of Cockpit 322.
Cockpit can connect to multiple machines from a single Cockpit session.
These are listed in the host switcher.
These additional machines are accessed via SSH from the machine that the
first machine connected to, and are xref:authentication.adoc[authenticated]
with the logged in user's password and/or SSH keys.
SSH host keys are stored in +/etc/ssh/ssh_known_hosts+.
The machine data is stored in +/etc/cockpit/machines.d/*.json+, or
below +$XDG_CONFIG_DIRS+ if set (see
link:./cockpit.conf.5.html[cockpit.conf]). Settings in lexicographically
later files amend or override settings in earlier ones. Cockpit itself
writes into +99-webui.json+; packages or admins who want to
pre-configure machines should ship files like +05-mymachine.json+ so
that changes from the web interface override the pre-configured files.
Each JSON file contains an object that maps machine IDs to objects that
define the properties of that machine. The ID can be a human readable
name or an IP address or any other unique value, and is shown in the web
interface until connecting to it the first time, at which point the web
interface will show the machine's host name.
The following properties are recognized:
+"address"+::
(string, mandatory) IP address or DNS name of the machine+"visible"+::
(boolean, optional) If +true+, the machine will be displayed and
available for managing with Cockpit. If +false+ (the default), it
will not be displayed, but still taken into account for type-ahead
search when adding new machines in the web interface.+"user"+::
(string, optional) User name on the remote machine. When not given,
Cockpit will default to the user name that was being used to log into
Cockpit itself.+"port"+::
(integer, optional) ssh port of the remote machine. When not given,
the default port 22 is used.+"color"+::
(string, optional) Color to assign to the machine label in the web
interface. This can be either given as
+rgb(r_value, g_value, b_value)+ with each value being an integer
between 0 and 255, or as a color name like +yellow+. When not given,
Cockpit will assign an unused color automatically.
Example:
....
{
"web server": {
"address": "192.168.2.4",
"visible": true,
"color": "rgb(100, 200, 0)",
"user": "admin"
},
"192.168.2.1": {
"address": "192.168.2.1",
"port": 2222,
"visible": true,
"color": "green"
}
}
....
Doc Modules Guide Pages Feature Networkmanager (`doc/modules/guide/pages/feature-networkmanager.adoc`)
[[feature-networkmanager]]
= NetworkManager
If available on the system, Cockpit uses
https://wiki.gnome.org/Projects/NetworkManager[NetworkManager] and the
DBus APIs it provides to interact with the system's network
configuration.
For non root users, NetworkManager controls access to its APIs via
xref:privileges.adoc[Policy Kit] and a user logged into Cockpit will have
the same permissions as they do from the command line.
To perform similar tasks from the command line, use the
https://fedoraproject.org/wiki/Networking/CLI[nmcli] command:
....
$ nmcli general status
STATE CONNECTIVITY WIFI-HW WIFI WWAN-HW WWAN
connected full enabled enabled enabled enabled
....
Devices marked as "not managed" with the +NM_CONTROLLED=no+ setting
will not be displayed in the interface.
Doc Modules Guide Pages Feature Packagekit (`doc/modules/guide/pages/feature-packagekit.adoc`)
[[feature-packagekit]]
= Package Updates
Cockpit uses the
https://www.freedesktop.org/software/PackageKit/gtk-doc/api-reference.html[PackageKit]
D-Bus API to get information about available package updates and to
apply them, in an Operating System independent manner.
To perform similar tasks from the command line, use the
https://www.freedesktop.org/software/PackageKit/pk-using.html[pkcon]
command:
....
$ pkcon refresh
$ pkcon get-updates
Available sudo-1.8.20p2-1.fc26.x86_64 (updates-testing)
Allows restricted root access for specified users
Available vim-filesystem-2:8.0.617-1.fc26.x86_64 (updates-testing)
VIM filesystem layout
Available vim-minimal-2:8.0.617-1.fc26.x86_64 (updates-testing)
A minimal version of the VIM editor
$ pkcon get-update-detail sudo
Details about the update:6.x86_64 [fedora]
Package: sudo-1.8.20p2-1.fc26.x86_64
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1452941
Update text: - update to 1.8.20p2
- added sudo package to dnf/yum protected packages
$ pkcon update
The following packages have to be updated:
sudo-1.8.20p2-1.fc26.x86_64 Allows restricted root access for specified users
vim-filesystem-2:8.0.617-1.fc26.x86_64 VIM filesystem layout
vim-minimal-2:8.0.617-1.fc26.x86_64 A minimal version of the VIM editor
Proceed with changes? [N/y] y
[...]
....
Of course you can also use your Operating System specific commands for
that, such as +dnf updateinfo info+ on Fedora or +sudo apt upgrade+
on Debian.
Doc Modules Guide Pages Feature Pcp (`doc/modules/guide/pages/feature-pcp.adoc`)
[[feature-pcp]]
= PCP Metrics
If available, Cockpit uses the https://pcp.io/[Performance Co-Pilot]
framework to gather metrics data about the system. This data is used to
display the history graphs on the "Metrics and history" page. Cockpit
can use the PCP logging feature to display archived data about the
system from a different point in time. If PCP is not available, then
Cockpit gathers the metrics data itself, but archival features are not
available.
Whether or not metrics are archived depends on whether the
https://linux.die.net/man/1/pmlogger[`+pmlogger.service+`] systemd unit
is running or not. The "Enable PCP metrics collector" button on the
Metrics page will enable and start this service.
To see similar metrics data from the command line, you can use tools
like https://linux.die.net/man/1/pmstat[`+pmstat+`] or
https://linux.die.net/man/1/pminfo[`+pminfo+`]:
....
$ pmstat
@ Sat Sep 26 15:30:10 2015
loadavg memory swap io system cpu
1 min swpd free buff cache pi po bi bo in cs us sy id
4.19 0 20710m 605148 6450m 0 0 0 2548 5688 14K 19 3 76
...
....
These metrics can also be exposed to other machines on a TCP port with
https://linux.die.net/man/1/pmproxy[pmproxy] and
https://redis.io/[Redis] or https://valkey.io/[Valkey]:
....
systemctl enable --now redis pmproxy
if you use firewalld, open port 44322:
firewall-cmd --permanent --add-service pmproxy
firewall-cmd --reload
....
This allows you to gather and visualize PCP metrics from multiple
machines with https://grafana.com/[Grafana] and the
https://grafana-pcp.readthedocs.io[PCP Grafana plugin].
Doc Modules Guide Pages Feature Realmd (`doc/modules/guide/pages/feature-realmd.adoc`)
[[feature-realmd]]
= realmd
If available on the system, Cockpit uses
https://www.freedesktop.org/software/realmd/[realmd] and the DBus APIs
it provides to configure the system's Active Directory or IPA domain
membership.
Not all systems can join all kinds of domains. This depends on the
availability of the necessary client software.
For non root users, realmd controls access to its APIs via
xref:privileges.adoc[Policy Kit] and a user logged into Cockpit will have
the same permissions as they do from the command line.
To perform similar tasks from the command line, use the
https://www.freedesktop.org/software/realmd/docs/realm.html[realm]
command:
....
$ realm join example.com
Password for Administrator:
....
http://www.freedesktop.org/software/realmd/[realmd] sets up
domain-qualified user names by default, i. e. login user names look like
"[email protected]+". For using unqualified names (just "+user+"),
set the +fully-qualified-names+ option in
https://www.freedesktop.org/software/realmd/docs/realmd-conf.html[/etc/realmd.conf]
before joining a domain.
Cockpit requests an SSL certificate from the IPA server for+cockpit-ws+ with the
https://www.freeipa.org/page/Certmonger[ipa-getcert] command.
Doc Modules Guide Pages Feature Selinux (`doc/modules/guide/pages/feature-selinux.adoc`)
[[feature-selinux]]
= SELinux Policy
If present on the system Cockpit can set the SELinux mode to enforcing
or permissive. It can also use +setroubleshootd+ to show audit issues
and apply suggested fixes.
To perform similar tasks from the command line use the +setenforce+
and +sealert+ tools.
To clear out all the information that +setroubleshootd+ tracks, you
can use a commands like:
....
$ sudo killall setroubleshootd
$ sudo rm -rf /var/lib/setroubleshoot/*
....
Doc Modules Guide Pages Feature Sosreport (`doc/modules/guide/pages/feature-sosreport.adoc`)
[[feature-sosreport]]
= SOS Report
If present on the system Cockpit can use +sosreport+ to collect system
configuration and diagnostic information.
To perform similar tasks from the command line, use the +sosreport+
command.
Doc Modules Guide Pages Feature Storaged (`doc/modules/guide/pages/feature-storaged.adoc`)
[[feature-storaged]]
= storaged
If available on the system, Cockpit uses
https://github.com/storaged-project/storaged/[`+storaged+`] to configure
and monitor storage, disks, mounts etc. on the system. This
functionality is present in the Cockpit storaged package.
The +storaged+ project is originally based on a project called
https://www.freedesktop.org/wiki/Software/udisks/[`+udisks+`] and added
support for many more features such as
https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)[LVM],
https://en.wikipedia.org/wiki/ISCSI[iSCSI],
https://en.wikipedia.org/wiki/Linux_DM_Multipath[Multipath], and
https://btrfs.wiki.kernel.org/index.php/Main_Page[BTRFS]. The same tools
and backwards compatible API are available between +storaged+ and+udisks+ the projects. Cockpit can use +udisks+ but disables many of
it's storage related features, including updating +/etc/fstab+ and+/etc/crypttab+ for stability reasons.
For non root users, storaged controls access to its APIs via
xref:privileges.adoc[Policy Kit] and a user logged into Cockpit will have
the same permissions as they do from the command line.
To perform similar tasks from the command line, use the +storagedctl+
command:
....
$ udisksctl dump
...
....
To perform LVM tasks, you may use the various LVM commands, such as+vgcreate+, +lvresize+ and so on. Cockpit will detect such changes
made at the command line.
Cockpit recognizes devices with multiple paths and can start the+multipathd+ service in case it is not running. On the command line,
you can control multipath features with the +mpathconf+,+multipathd+, and +multipath+ commands.
To manage iSCSI initiators from the command line, you can use+iscsiadm+ and related tools.
Doc Modules Guide Pages Feature Systemd (`doc/modules/guide/pages/feature-systemd.adoc`)
[[feature-systemd]]
= systemd
Cockpit uses https://www.freedesktop.org/wiki/Software/systemd/[systemd]
and the DBus APIs it provides to configure and monitor core aspects of
the system. Use of alternate system APIs are not currently implemented.
For non root users, systemd controls access to its APIs via
xref:privileges.adoc[Policy Kit] and a user logged into Cockpit will have
the same permissions as they do from the command line.
Cockpit retrieves information about the host and changes the hostname
via the +hostnamed+ daemon. To perform similar tasks from the command
line use the
https://www.freedesktop.org/software/systemd/man/hostnamectl.html[`+hostnamectl+`]
command:
....
$ hostnamectl
Static hostname: pink.example.com
Pretty hostname: Pink
Icon name: computer-desktop
Chassis: desktop
Machine ID: ef00b79be229463cbb844c3e715de96c
Boot ID: 934983d64d34465cb5a8383b5a89ad8c
Operating System: Fedora 22 (Twenty Two)
CPE OS Name: cpe:/o:fedoraproject:fedora:22
Kernel: Linux 4.0.4-301.fc22.x86_64
Architecture: x86-64
....
Cockpit configures the system time and time zone via the +timedated+
daemon. To perform similar tasks from the command line use the
https://www.freedesktop.org/software/systemd/man/timedatectl.html[`+timedatectl+`]
command:
....
$ timedatectl list-timezones
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
...
....
Cockpit can manage the list of NTP servers used by +systemd-timesyncd+
by putting its own file into +/etc/systemd/timesyncd.conf.d/+. Note
that +systemd-timesyncd+ is not always enabled, depending on the
configuration of the machine. In that case, Cockpit disabled the UI for
managing the list of NTP servers. In some cases use of +ntpd+ can
cause the +timedated+ daemon to behave inconsistently with regards to
time synchronization.
Cockpit reboots or powers down the machine by using the
https://www.freedesktop.org/software/systemd/man/shutdown.html[`+shutdown+`]
command. To perform similar tasks from the command line, run it
directly:
....
$ sudo shutdown +15
Shutdown scheduled for Sa 2015-09-26 15:49:40 CEST, use 'shutdown -c' to cancel.
....
Cockpit manages system services and sockets via systemd. To perform
similar tasks from the command line use the
https://www.freedesktop.org/software/systemd/man/systemctl.html[`+systemctl+`]
command:
....
$ systemctl status cockpit
● cockpit.service - Cockpit Web Service
Loaded: loaded (/usr/lib/systemd/system/cockpit.service; static; vendor preset: disabled)
Drop-In: /etc/systemd/system/cockpit.service.d
└─debug.conf
Active: active (running) since Sa 2015-09-26 13:28:02 CEST; 2h 7min ago
Docs: man:cockpit-ws(8)
Main PID: 6957 (cockpit-ws)
Memory: 1.8M
CGroup: /system.slice/cockpit.service
├─ 6957 /usr/libexec/cockpit-ws
└─29598 /usr/bin/ssh-agent
....
In order to customize who can perform various actions in system,
xref:privileges.adoc#privileges-polkit[create polkit rules] with the following actions
and details:
+org.freedesktop.systemd1.manage-units+::
Permission to manage system services or other units. Details
available: +unit+, +verb++org.freedesktop.systemd1.manage-unit-files+::
Permission to manage system services or other unit files.+org.freedesktop.systemd1.reload-daemon+::
Permission to reload the systemd state.
For example, placing the following polkit rule to+/etc/polkit-1/rules.d/10-http.rule+ allows all users in the+operators+ group start, stop, and restart the Apache HTTP service:
....
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units") {
if (subject.isInGroup("operators") && action.lookup("unit") == "httpd.service") {
var verb = action.lookup("verb");
if (verb == "start" || verb == "stop" || verb == "restart") {
return polkit.Result.YES;
}
}
}
});
....
Doc Modules Guide Pages Feature Terminal (`doc/modules/guide/pages/feature-terminal.adoc`)
[[feature-terminal]]
= Terminal
Cockpit provides a standard shell in a terminal. This shell and the
processes running in it have the xref:privileges.adoc[same privileges] as if
the user had logged in via SSH.
Doc Modules Guide Pages Feature Tuned (`doc/modules/guide/pages/feature-tuned.adoc`)
[[feature-tuned]]
= Tuned Profiles
If present on the system Cockpit can use Tuned and the DBUS API it
provides to set system performance profiles. To perform similar tasks
from the command line, use the +tuned-adm+ command.
Doc Modules Guide Pages Feature Users (`doc/modules/guide/pages/feature-users.adoc`)
[[feature-users]]
= User Tools
Cockpit uses the usual tools to create and modify local user accounts.
Examples are +useradd+, +usermod+ and +passwd+. These same tools
are available for use on the command line.
Doc Modules Guide Pages Features (`doc/modules/guide/pages/features.adoc`)
[[features]]
= Feature Internals
:leveloffset: +1
include::feature-systemd.adoc[]
include::feature-journal.adoc[]
include::feature-networkmanager.adoc[]
include::feature-firewall.adoc[]
include::feature-storaged.adoc[]
include::feature-users.adoc[]
include::feature-realmd.adoc[]
include::feature-terminal.adoc[]
include::feature-pcp.adoc[]
include::feature-machines.adoc[]
include::feature-selinux.adoc[]
include::feature-tuned.adoc[]
include::feature-sosreport.adoc[]
include::feature-packagekit.adoc[]
:leveloffset: -1
Doc Modules Guide Pages Https (`doc/modules/guide/pages/https.adoc`)
[[https]]
= SSL/TLS Usage
Cockpit usually requires that web browsers communicate with it using
HTTPS, for security reasons.
[[https-required]]
== HTTPS Requirement
Cockpit listens for both HTTP and HTTPS connections on the same port, by
default 9090. If an HTTP connection is made, Cockpit will redirect that
connection to HTTPS. There are some exceptions:
- If an HTTP connection comes from
+localhost+(+127.0.0.1+or+::1+, then Cockpit will allow communication without redirecting to
HTTPS. - Certain URLs, like
+/ping+are not required to use HTTPS.
This behavior can be overridden by setting the +AllowUnencrypted+
option in +cockpit.conf+.
[[https-certificates]]
== Certificates
Cockpit will load a certificate from the +/etc/cockpit/ws-certs.d+,
directory, or below +$XDG_CONFIG_DIRS+ if set (see
link:./cockpit.conf.5.html[cockpit.conf]). It will use the last file
with a +.cert+ or +.crt+ extension in alphabetical order. The file
should contain one or more OpenSSL style +BEGIN CERTIFICATE+ blocks
for the server certificate and the intermediate certificate authorities.
The private key must be contained in a separate file with the same name
as the certificate, but with a +.key+ suffix instead. The key must not
be encrypted.
If no certificate is found, a self-signed certificate is created and
stored in the +0-self-signed.cert+ file. On some platforms, Cockpit
will also generate a ca.crt in that directory, which may be safely
imported into client browsers.
Cockpit will read the files as root, so they can have tight permissions.
To check which certificate +cockpit-ws+ will use run the following
command.
....
$ sudo /usr/libexec/cockpit-certificate-ensure --check
....
Or, on Debian-based systems:
....
$ sudo /usr/lib/cockpit/cockpit-certificate-ensure --check
....
If using +certmonger+ to manage certificates, following command can be
used to automatically prepare a certificate/key file pair:
....
getcert request -f /etc/cockpit/ws-certs.d/50-certmonger.cert
-k /etc/cockpit/ws-certs.d/50-certmonger.key
-D myhostname.example.com
[--ca=...]
....
This will not work on Red Hat Enterprise Linux 8 by default. Adjust the
SELinux type of the certificate directory to +cert_t+ to allow
certmonger to write its certificates there:
....
semanage fcontext -a -t cert_t '/etc/cockpit/ws-certs.d(/.*)?'
restorecon -v /etc/cockpit/ws-certs.d
....
Doc Modules Guide Pages Index (`doc/modules/guide/pages/index.adoc`)
[[guide]]
= Deployment Guide
Documentation for Cockpit {cockpit-version}.
Latest version https://docs.cockpit-project.org/[available here].
Doc Modules Guide Pages Listen (`doc/modules/guide/pages/listen.adoc`)
[[listen]]
= TCP Port and Address
Cockpit's +cockpit-ws+ component is configured by default to accept
connections on port +9090+. This is the port that is documented for a
"Web-based System Manager" to listen on. It is also relatively
memorable.
However there are many reasons you may wish to change the default port.
For example other software may use port +9090+ or you may wish to
setup Cockpit to listen on +443+ instead. It is also possible to have
Cockpit only listen on one specific IP address.
Note that it is only required to have Cockpit listening on a TCP port on
the server that you access with your web browser. If you add multiple
servers with host switcher, Cockpit will connect to those servers via+ssh+.
The systems that Cockpit runs on are typically locked down with
firewalls, SELinux, so changing the default port is not as easy as
editing a configuration file.
[[listen-systemd]]
== Cockpit systemd Socket
On servers with
https://www.freedesktop.org/wiki/Software/systemd/[`+systemd+`] Cockpit
starts on demand via socket activation. To change its port and/or
address you should place the following content in the+/etc/systemd/system/cockpit.socket.d/listen.conf+ file. Create the
file and directories in that path which not already exist. The+ListenStream+ option specifies the desired address and TCP port.
....
[Socket]
ListenStream=
ListenStream=443
....
....
[Socket]
ListenStream=
ListenStream=7777
ListenStream=192.168.1.1:443
FreeBind=yes
....
NOTE: The first line with an empty value is intentional. +systemd+
allows multiple +Listen+ directives to be declared in a single socket
unit; an empty value in a drop-in file resets the list and thus disables
the default port 9090 from the original unit.
The +FreeBind+ option is highly recommended when defining specific IP
addresses. See the
https://www.freedesktop.org/software/systemd/man/systemd.socket.html[`+systemd.socket+`
manpage] for details.
In order for the changes to take effect, run the following commands:
....
$ sudo systemctl daemon-reload
$ sudo systemctl restart cockpit.socket
....
[[listen-selinux]]
== SELinux Port
If https://selinuxproject.org/page/Main_Page[SELinux] is protecting your
server, then you will need to tell it to allow Cockpit to listen on the
new port. Run the following command to do so. The last argument
specifies the desired TCP port.
....
$ sudo semanage port -a -t websm_port_t -p tcp 9999
....
If the port is already defined by some other part of the SELinux policy,
then you will need to use the +-m+ argument to modify the definition.
That's the case with the +443+ SSL port, which is typically defined as
an +http_port_t+ port.
....
$ sudo semanage port -m -t websm_port_t -p tcp 443
....
The changes should take effect immediately.
[[listen-firewalld]]
== Firewalld Port
If https://fedoraproject.org/wiki/FirewallD[Firewalld] is configured as
your firewall, then you will need to tell it to allow Cockpit to receive
connections on the new port. Run the following commands to do so. The
last options specify the desired TCP port.
....
$ sudo firewall-cmd [--zone=ZONE] --add-port=443/tcp
$ sudo firewall-cmd --permanent [--zone=ZONE] --add-port=443/tcp
....
Doc Modules Guide Pages Multi Host (`doc/modules/guide/pages/multi-host.adoc`)
[[multi-host]]
= Managing multiple hosts at the same time
[WARNING]
This feature is deprecated as of Cockpit 322.
Cockpit allows you to access multiple hosts in a single session, by
establishing SSH connections to other hosts. This is quite similar to
logging into these other hosts using the +ssh+ command on the command
line, with one very important difference:
Code from the local host and all the remote hosts run at the same time,
in the same browser context. They are not isolated from each other in
the browser. All code effectively has the same privileges as the primary
session on the local host.
Thus, you should only only connect to remote hosts that you trust. You
must be sure that none of the hosts that you connect to will cause
Cockpit to load malicious JavaScript code into your browser.
Therefore, Cockpit will warn you before connecting to more than one
host. It is also possible to disable multiple hosts entirely, and some
operating systems do this already by default.
You can prevent loading of JavaScript, HTML, etc from more than one host
by adding this to +cockpit.conf+:
....
[WebService]
AllowMultiHost=false
....
When you allow multiple hosts in a single Cockpit session by setting+AllowMultiHost+ to true, then the user will be warned once per
session, before connecting to the second host. If that is still too
much, you can switch the warning off completely by adding the following
to +cockpit.conf+:
....
[Session]
WarnBeforeConnecting=false
....
Doc Modules Guide Pages Packages (`doc/modules/guide/pages/packages.adoc`)
[[packages]]
= Cockpit Packages
Cockpit is separated into various packages, each of which brings
specific features and/or code.
[WARNING]
In addition, any APIs or behavior not explicitly documented here is an internal API and can be changed at any time.
[[package-layout]]
== Layout of Package Files
A package consists of one or more files placed in a directory or its
subdirectories. It must have a +manifest.json+ file and follow certain
naming conventions.
The name of a package is the name of the directory.
The name of the package must be ASCII alphanumeric, and may contain an
underscore. Names of directories and files in the package must consist
of ASCII alphanumeric along with dash, underscore, dot, and comma. No
spaces are allowed.
Cockpit uses the data directories from the
https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
Base Directory Specification] to locate packages. The +$XDG_DATA_DIRS+
represents a colon separate list of system data directories, and+$XDG_DATA_HOME+ is a user specific data directory. If the environment
variables are not set, defaults are used, according to the spec. If
cockpit has been built with an alternate +--prefix=/path+ then the+$prefix/share/cockpit+ is used by default.
A +cockpit/+ subdirectories in any of these data directories is the
location where packages are loaded by Cockpit. If Cockpit finds a
package with the same name, in multiple data directories, then the first
one wins. According to the spec the first data directory is+$XDG_DATA_HOME+ and then +$XDG_DATA_DIRS+ in order.
This means that, by default the following directories are searched for
cockpit packages, and in this order:
+~/.local/share/cockpit/++/usr/local/share/cockpit/++/usr/share/cockpit/+
Packages placed in +$XDG_DATA_HOME+ are not cached by Cockpit or the
web browser. Other packages are cached aggressively, and are accessed
using a checksum of the files in the packages and their names.
You can use the following command to list the packages installed on a
server. You'll note that it's output may change when you run the command
as different users, if there are packages installed in the user's home
directory.
....
$ cockpit-bridge --packages
...
....
To further clarify things, here is an example package called
"my-package" and its file layout:
....
/usr/share/cockpit/
my-package/
manifest.json
file.html
some.js
....
Place or symlink packages in your +~/.local/share/cockpit+ directory
(or appropriate +$XDG_DATA_HOME+ location) that you would like to
modify and develop. System installed packages should not change while
Cockpit is running.
== Package Manifest
Each package has a +manifest.json+ file. It is a JSON object. The
following fields may be present in the manifest:
content-security-policy::
By default Cockpit serves packages using a strict
https://en.wikipedia.org/wiki/Content_Security_Policy[Content Security
Policy], which among other things does not allow inline styles or
scripts. This can be overridden on a per-package basis, with this
setting.
+
If the overridden content security policy does not contain a
+default-src+, +connect-src+, +base-uri+, +form-action+,
+object-src+, or +block-all-mixed-content+ then these will be
added to the policy from the manifest.
name::
An optional string that changes the name of the package. Normally
packages derive their name from the directory that they are located
in. This field overrides that name.
priority::
An optional integer that specifies which package is preferred in cases
where there are conflicts. For example given two packages with the
same +name+ a package is chosen based on its priority. A higher integer
means higher priority.
conditions::
An optional list of +{"predicate": "value"}+ objects. Cockpit will
only consider the package if all conditions are met. This is
preferable to using +priority+. +
+
Currently supported predicates are +path-exists+ and +path-not-exists+
(since Cockpit 286) and +any+ (since Cockpit 348). +any+ accepts a
list of conditions and evaluates to true if at least one of the nested
conditions is met. Unknown predicates are ignored.
requires::
An optional JSON object that contains a +"cockpit"+ string version
number. The package will only be usable if the Cockpit bridge and
javascript base are equal or newer than the given version number.
version::
An informational version number for the package.
preload::
A list of identifiers of the components that should be preloaded.
Normally, the files of a component are loaded when the user navigates
to it for the first time. The files of a preloaded component are
loaded immediately after the user logs in, and the initialization code
of the component is invoked.
+
The value of this field is an array of strings, where each string is
one of the keys used in the +dashboard+, +menu+, or +tools+
fields that are explained below.
parent::
This option is used when module does not have its own menu item but is
a part of a different module. This is described by JSON object with
properties +component+ which takes name of the superordinate
component and +docs+ with list of documentation URLs for the given
page. See below for structure of +docs+ property.
In addition, the following keys contain information about where
components of the package should appear in Cockpit's user interface.
Each of these keys is optional and contains an object mapping unique
identifiers to menu items, which are described below. (The naming of
these fields doesn't perfectly match the current user interface for
historical reasons.)
dashboard::
Dashboard items appear in the menu under the section Apps.
menu::
These items appear in the menu under the section System. This
section is roughly ordered into these categories (with their order
in parentheses):
+
- System Information (10)
- Logs (20)
- Configuring major subsystems (30-40)
- Things running on the machine (VMs, Containers - 50-60)
- Implementation Details (Accounts, Services - 70-100)
tools::
These items appear in the menu under the section Tools.
Menu items and tools are registered using JSON objects that have the
following properties:
label::
The label for the menu item or tool.
order::
An optional order integer to place this menu item or tool. Lower
integers are listed first.
path::
The relative path to the HTML file within the package that implements
the menu item or tool.
docs::
List of documentation URLs for the given page. Each item is an object
containing +label+ and +url+.
keywords::
Keywords that describe the page and which are used for searching.
These keywords should be lowercase. Keywords is a list containing
keyword items as described below. Page label is prepended as first
keyword in the first keyword item.
Keyword items are registered using JSON objects that have the following
properties:
matches::
List of keywords to be matched.
goto::
Optional path that is used for all keywords in this item. When this
argument starts with slash, then it is used as pathname, otherwise it
is used as hash. Defining +goto:"page_hash"+ in page with
+path:"/page_path"+ would redirect to +/page_path#page_hash+,
while +goto:"/page_path"+ would redirect to +/page_path+ ignoring
default page path.
weight::
How much keywords are prioritized over others. Default is 3.
translate::
+false+ when keywords should not be localized. Default is +true+.
An example manifest.json with some optional properties set:
....
{
"version": 0,
"require": {
"cockpit": "120"
},
"tools": {
"mytool": {
"label": "My Tool",
"path": "tool.html"
}
}
}
....
....
{
"version": 0,
"require": {
"cockpit": "120"
},
"conditions": [
{"path-exists": "/usr/bin/mytool"},
{"path-exists": "/etc/mytool.conf"},
{"path-not-exist": "/etc/incompatible-tool"}
],
"tools": {
"mytool": {
"label": "My Tool",
"path": "tool.html"
}
}
}
....
The following example shows how to use disjunctive conditions with +any+
to specify alternative requirements (available since Cockpit 348). This
package will be available if either +/usr/bin/alt1+ or +/usr/bin/alt2+
exists. In any case, +/etc/incompatible-tool+ must not exist.
....
"conditions": [
{"any": [{"path-exists": "/usr/bin/alt1"}, {"path-exists": "/usr/bin/alt2"}]},
{"path-not-exists": "/etc/incompatible-tool"}
]
....
[[package-manifest-override]]
== Manifest overrides
To change a manifest system-wide, a file+.override.json+ may be placed into+/etc/cockpit/+, or below +$XDG_CONFIG_DIRS+ if set (see
link:./cockpit.conf.5.html[cockpit.conf]). To change it for a particular
user only, put the override into +~/.config/cockpit+.
These override the information in the manifest in the simple
https://tools.ietf.org/html/rfc7386[JSON Merge Patch] format.
This can be used to hide or modify menu items of an existing package.
For example +/etc/cockpit/systemd.override.json+ or+~/.config/cockpit/systemd.override.json+ could hide the Logs menu
item and move the Services menu item to the top of the menu.
....
{
"menu": {
"logs": null,
"services": {
"order": -1
}
}
}
....
[[package-links]]
== Package Links and Paths
When referring to files in your package, such as in a hyperlink or a+<style>+ tag or +<script>+ tag, simply use a relative path, and
refer to the files in the same directory. When you need to refer to
files in another package use a relative link.
For example here's how to include the base +cockpit.js+ script in your
HTML from the +latest+ package:
....
....
Do not assume you can link to any file in any other package. Refer to
the xref:development.adoc[list of API packages] for those that are available
for use.
[[package-minified]]
== Content Negotiation
In order to support gzipped and/or minified data, the files in a package
are loaded using content negotiation logic. A HTTP request for the file+test.js+ in the package named +mypackage+ will return+mypackage/test.js+ or +mypackage/test.js.gz+ (in undefined
preference). If neither exists, then it returns+mypackage/test.js.min+ or +mypackage/test.js.min.gz+ (again in
undefined preference).
When packages are loaded from a system directory, Cockpit optimizes the
file system lookups above, by pre-listing the files. This is one of the
reasons that you should never change packages installed to a system
directory while Cockpit is running.
[[package-api]]
== Using Cockpit API
Cockpit has API available for writing packages. There is no API
available for external callers to invoke via HTTP, REST or otherwise.
API from various packages can be used to implement Cockpit packages.
Each package listed here has some API available for use. Only the API
explicitly documented should be used.
- xref:development.adoc[API Listing]
To include javascript from the API, simply load it into your HTML using
a script tag. Alternatively you can use an javascript loader.
[[package-bridges]]
== Bridges for specific tasks
On the server side the man:cockpit-bridge[1]
connects to various system APIs that the front end UI requests it to.
There are additional bridges for specific tasks that the main+cockpit-bridge+ cannot handle, such as using the PCP C library API.
These additional bridges can be registered in a +"bridges"+ section of
a package's +manifest.json+ file. Building such a bridge is a complex
tasks, and we will skip over that here. However it is useful to adjust
how these additional bridges are called, and so we'll look at how they
are registered.
An example +manifest.json+ with a bridges section:
....
{
"bridges": [
{
"match": { "payload": "metrics1" },
"spawn": [ "/usr/libexec/cockpit-pcp" ]
}
]
}
....
The bridges are considered in the order they are listed in the array.
Use the +manifest.json+``+"priority"+ field to control order between
packages. The bridges are registered using JSON objects that have the
following properties:
environ::
Optional, additional environment variables to pass to the bridge
command.
match::
The +"match"+ object describes which channel open command options
need to match for a given channel to be handed over to this bridge.
privileged::
If set to +true+, this marks the bridge as a superuser bridge.
Cockpit will start one of these explicitly when trying to escalate the
privileges of a session. A privileged bridge can not have a
+"match"+ property.
label::
Setting this enables selection of privileged bridges in the UI. When
no privileged bridge has a +label+, then Cockpit will start the
bridge that runs +sudo+. This is the case in a default Cockpit
installation. When at least one privileged bridge has a +label+ then
the user can select one of them when escalating privileges. As a
special case, if only one bridge has a +label+, then the step of
selecting a bridge is omitted in the UI and that one bridge is always
started.
+
Thus, if you add a privileged bridge with a +label+ in a new
manifest, Cockpit will use that bridge the next time a user opens the
"Administrative access" dialog. If you want the user to choose between
the +sudo+ method and your new one, you need to duplicate the
+sudo+ bridge definition in your manifest and give it a label.
problem::
If a problem is specified, and this bridge fails to start up then
channels will be closed with this problem code. Otherwise later
bridges or internal handlers for the channel will be invoked.
spawn::
The command and arguments to invoke.
The +spawn+ and +environ+ values can be dynamically taken from a
matching open command values. When a value in either the +spawn+ or+environ+ array contains a named variable wrapped in +${}+, the
variable will be replaced with the value contained in the matching open
command. Only named variables are supported and name can only contain
letters, numbers and the following symbols: +._-+
For example a bridges section like:
....
{
"bridges": [
{
"match": { "payload": "example" },
"environ": [ "TAG=${tag}" ],
"spawn: [ "/example-bridge", "--tag", "${tag}" ],
"problem": "access-denied"
}
]
}
....
when a open command is received with a payload of +example+ with+tag+ value of +tag1+. The following command will be spawned
....
TAG=tag1 /example-bridge --tag tag1
....
Processes that are reused so if another open command with a "tag" of+tag1+ is received. The open command will be passed to existing
process, rather than spawning a new one. However a open command with an
tag of +tag2+ will spawn a new command:
....
TAG=tag2 /example-bridge --tag tag2
....
If you need to include +${}+, as an actual value in your arguments you
can escape it by prefixing it with a +\+
[[package-replace]]
== Replacing an existing package
If the functionality in a package replaces that of another package then
it can replace that package by claiming the same +name+ and a higher+priority+.
For example, a package in the +/usr/share/cockpit/disks+ directory
could replace Cockpit's storage package with a +manifest.json+ like
this:
....
{
"version": 0,
"name": "storage",
"priority": 10,
"menu": {
"index": {
"label": "Disk Storage",
"order": 15
}
}
}
....
Doc Modules Guide Pages Privileges (`doc/modules/guide/pages/privileges.adoc`)
[[privileges]]
= Privileges and Permissions
When a user is logged into Cockpit, they are logged into a normal
session that has exactly the same privileges as if they logged in via
SSH or on the console.
However, Cockpit will usually try to escalate the privileges of the user
using https://www.freedesktop.org/wiki/Software/polkit/[Policy Kit] or
https://www.sudo.ws/[sudo]. If the user is able to escalate privileges
from the command line by typing in their password again (or without
typing in any password), then Cockpit will be able to escalate the
privileges of the session to "root" immediately upon login.
The user can change the privileges of a session from within that
session, via the "Administrative access" indicator in the top bar. From
that indicator, the user can drop "root" privileges and regain them. On
the next login, Cockpit will give the session the same privileges.
Usually a user needs to be in the +wheel+ Unix user group for the user
to be able to escalate privileges in this way. However both Policy Kit
and sudo may be configured to use other criteria.
[[privileges-polkit]]
== Customizing Polkit Privileges
Services like
https://www.freedesktop.org/wiki/Software/systemd/[systemd] and
https://wiki.gnome.org/Projects/NetworkManager[NetworkManager] use
https://www.freedesktop.org/wiki/Software/polkit/[Polkit] to validate
and escalate privileges. It is possible to customize these rules with
files in +/etc/polkit-1/rules.d+.
Polkit rules files are
https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html[javascript
with specific methods and objects]. For example, placing the following
polkit rule to +/etc/polkit-1/rules.d/10-operators.rule+ allows all
users in the +operators+ group to start, stop, restart and otherwise
manage systemd services:
....
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units") {
if (subject.isInGroup("operators")) {
return polkit.Result.YES;
}
}
});
....
In order to allow a certain group to perform any administrative action
you could add a rule like this:
....
polkit.addAdminRule(function(action, subject) {
return ["unix-group:operators"];
});
....
Doc Modules Guide Pages Sso (`doc/modules/guide/pages/sso.adoc`)
[[sso]]
= Single Sign On
Cockpit can use Kerberos for Single Sign On authentication, where users
are automatically authenticated if they have a valid Kerberos ticket.
[[sso-server]]
== Server Requirements
To authenticate users, the server that Cockpit is running on must be
joined to a domain. This can usually be accomplished using the
https://freedesktop.org/software/realmd/docs/realm.html[`+realm join example.com+`]
command.
The domain must be resolvable by DNS. For instance, the SRV records of
the kerberos server should be resolvable:
....
$ host -t SRV _kerberos._udp.example.com
_kerberos._udp.example.com has SRV record 0 100 88 dc.example.com
....
The server running Cockpit should have a fully qualified name that ends
with the domain name.
There must be a valid Kerberos host key for the server in the+/etc/krb5.keytab+ file. Alternatively, if you would like to use a
different keytab, you can do so by placing it in+/etc/cockpit/krb5.keytab+, or below +$XDG_CONFIG_DIRS+ if set (see
link:./cockpit.conf.5.html[cockpit.conf]). It may be necessary to create
a kerberos service principal and update the keytab if it is not present.
Depending on your domain type different service names are required:
Active Directory::
+HOST/[email protected]+
IPA and MIT::
+HTTP/[email protected]+
When joining an IPA domain with Cockpit and the +ipa+ command line
tool is available, both the service principal name and a+/etc/cockpit/krb5.keytab+ get created automatically, so that Kerberos
based single sign on into Cockpit works out of the box. If you want/need
to do this by hand or in a script, first create or modify the +HTTP/+
service principal:
....
$ sudo ipa service-add --ok-as-delegate=true --ok-to-auth-as-delegate=true
HTTP/[email protected]
or, if it already exists, just enable delegation:
$ sudo ipa service-mod --ok-as-delegate=true --ok-to-auth-as-delegate=true
HTTP/[email protected]
....
Then generate a key for that principal:
....
$ sudo ipa-getkeytab -p HTTP/[email protected] -k /etc/cockpit/krb5.keytab
....
The following command can be used to list the+/etc/cockpit/krb5.keytab+:
....
$ sudo klist -k /etc/cockpit/krb5.keytab
....
Lastly accounts from the domain must be resolvable to unix accounts on
the server running Cockpit. For example:
....
$ getent passwd [email protected]
[email protected]:*:381001109:381000513:User Name:/home/user:/bin/sh
....
If you wish to delegate your kerberos credentials to Cockpit, and allow
Cockpit to then connect to other machines using those credentials, you
should enable delegation for the hosts running Cockpit, and in some
cases the +HTTP+ service as well. When joining an IPA domain, this is
enabled by default.
Domain admins (usually the [email protected]+ group) should normally
also be able to administer any joined machine. Enable sudo access for
that group with the following command on the IPA server, for version
4.7.1 and later:
....
ipa-advise enable-admins-sudo | sh -ex
....
On earlier FreeIPA versions, run these commands instead, as a domain
admin on any joined machine:
....
ipa sudorule-add --hostcat=all --cmdcat=all All
ipa sudorule-add-user --groups=admins All
....
Note that this does not change security properties; domain admins can
give this privilege to themselves, so it is safe to enable by default.
[[sso-client]]
== Client Requirements
The client side, where your web browser is running, should have a valid
kerberos ticket in the current user session. A command like this will
get one:
....
$ kinit [email protected]
Password for [email protected]:
....
In addition your browser must be usually be configured to allow kerberos
authentication for the domain.
Mozilla Firefox::
Go to +about:config+ and set the
+network.negotiate-auth.trusted-uris+ setting to your domain name
preceded by a dot, ie: +.example.com+
Google Chrome::
On Linux: create the file
+/etc/opt/chrome/policies/managed/example-com.json+ with the
contents:
{empty}::
....
{
"AuthServerWhitelist": "*example.com"
}
....
{empty}::
and restart the browser. On other platforms, exit your browser
completely, and start it with a command line like this:
+google-chrome --auth-server-whitelist=*example.com+
Use a fully qualified server name (with the domain name at the end) to
access Cockpit in your web browser.
If you wish to connect from one server to another in Cockpit using
kerberos SSO, then you have to explicitly enable all sorts of things.
For starters, make sure that delegated credentials are allowed by your
domain (see above). Next when requesting your kerberos ticket make sure
that forwardable tickets are requested:
....
$ kinit -f [email protected]
Password for [email protected]:
....
Make sure that the forwardable flag +F+ is present in your ticket:
....
$ klist -f
Ticket cache: KEYRING:persistent:1000:1000
Default principal: [email protected]
Valid starting Expires Service principal
18.03.2017 05:39:23 19.03.2017 05:39:20 krbtgt/[email protected]
Flags: FIA
....
Lastly configure your browser to allow delegated, forwardable kerberos
credentials to be sent to Cockpit:
Mozilla Firefox::
Go to +about:config+ and set the
+network.negotiate-auth.delegation-uris+ setting to your domain name
preceded by a dot, ie: +.example.com+
Google Chrome::
On Linux: create the file
+/etc/opt/chrome/policies/managed/example-com.json+ with the
contents:
{empty}::
....
{
"AuthServerWhitelist": "*example.com",
"AuthNegotiateDelegateWhitelist": "*example.com"
}
....
{empty}::
and restart the browser. On other platforms, exit your browser
completely, and start it with a command line like this:
+google-chrome --auth-server-whitelist=*example.com --auth-negotiate-delegate-whitelist=*example.com+
Doc Modules Guide Pages Startup (`doc/modules/guide/pages/startup.adoc`)
[[startup]]
= Start up
Cockpit's +cockpit-ws+ component is what the browser connects to and
it typically starts on demand via
https://www.freedesktop.org/wiki/Software/systemd/[`+systemd+`] socket
activation.
The actual +cockpit.service+ and +cockpit-ws+ process will start on
demand when a browser accesses the +cockpit.socket+,
xref:listen.adoc[usually on port 9090]. Once a user logs in then a+cockpit-bridge+ process will be started in a Linux user login
session.
Only systems that you connect to with your browser need to have the+cockpit.socket+ enabled. For systems that you add through host
switcher the bridge is started via SSH on demand.
[[startup-shutdown]]
== Process exit
The +cockpit-bridge+ process will exit when the user logs out. In
addition, after 10 minutes of inactivity, the +cockpit-ws+ process
will exit on its own. The browser will automatically disconnect if it
fails to hear from the +cockpit-ws+ process for 30 seconds.
[[startup-boot]]
== Boot start up
To make Cockpit available by default after system boot the+cockpit.socket+ needs to be enabled:
....
$ sudo systemctl enable cockpit.socket
....
If you wish to not have Cockpit available by default via a browser, then
the +cockpit.socket+ should be disabled:
....
$ sudo systemctl disable cockpit.socket
....
Doc Modules Guide Pages Urls (`doc/modules/guide/pages/urls.adoc`)
[[urls]]
= Cockpit URLs
Cockpit URLs follow a specific structure, related to the components they
are loading. Various components are loaded in ++ tags. The
URLs for these components are described first. Further down below you
can find information about the top level bookmarkable Cockpit address
URLs.
[[urls-components]]
== Component URLs
Cockpit components are HTML documents. They are organized into
xref:packages.adoc[packages]. Each package contains information about which
HTML components are available in that package. Components should always
use relative URLs to access resources, such as images, scripts or CSS
files, even if they refer to a resource in another package.
The following are valid component URLs, each bit will be discussed
below:
....
/cockpit/@localhost/package/component.html#/hash
/cockpit/$checksum/package/component.html#/hash
/cockpit/@server.example.com/package/component.html#/hash
/cockpit+embedder/@localhost/package/component.html#/hash
....
All resource URLs are under the +/cockpit+ namespace. In cases where a
Cockpit component is being xref:embedding.adoc[embedded] the +/cockpit+
may be followed by a plus sign and another +embedder+ specific
identifier.
What follows is either a +@host+ or +$checksum+ which tells cockpit
where to xref:packages.adoc[find the package]. Checksums are used when more
than one host has identical packages and the resources can be cached.
The +package+ name is next, followed by the +component+ HTML path
inside that package. And lastly a hash allows for navigation within a
single component. The hash should follow a URL path and/or query string
form.
[WARNING]
Never assume that the `+@host+` or `+$checksum+` portion is predictable. Only refer to resources in packages on the same host.
[[urls-visible]]
== Visible URLs
The above Component URLs are usually not visible to the user. Instead
the Cockpit Web Service wraps the components in a shell which allows
navigation, and provides bookmarkable clean URLs to the component. These
URLs do not affect xref:embedding.adoc[embedders] or
xref:packages.adoc[components] directly.
If no path is present then the Cockpit will redirect to the default page
for the server.
If the first segment of the path begins with an +@+ sign, then the
component is being shown from a non-local host.
The next segment of the path, (or first if the component is being shown
on the local host) is the xref:packages.adoc[package name]. The remainder of
the path is a component file in the package. If no further path segments
are present, a default +index.html+ component in the package is
loaded. An extension of +.html+ is automatically appended.
The hash portion of the path is automatically transferred to the
component as the hash of its link:#urls-components[resource URL].
Doc Modules Man Pages Cockpit Bridge.1 (`doc/modules/man/pages/cockpit-bridge.1.adoc`)
= cockpit-bridge(1)
== Name
cockpit-bridge - Cockpit Host Bridge
== Synopsis
cockpit-bridge [--help] [--packages]
== Description
The cockpit-bridge program is used by Cockpit to relay messages and
commands from the Web front end to the server. Among other things it
relays DBus, and spawns processes on behalf of the Web user interface.
This program is not routinely run by users or administrators. It is in
the $PATH so that Cockpit can find it when connecting between hosts.
However there are some diagnostics available when running from the
command line.
== Options
--help::
Show help options.
--interact=boundary::
Interact with the raw cockpit1 protocol. Useful for debugging and
testing. Specify a boundary which should be on an empty line
between messages.
--packages::
List all available Cockpit packages and exit. Note this includes
packages available to the user running this command.
--version::
Show Cockpit version information.
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-ws[8]
Doc Modules Man Pages Cockpit Desktop.1 (`doc/modules/man/pages/cockpit-desktop.1.adoc`)
= cockpit-desktop(1)
== Name
cockpit-desktop - Cockpit Desktop integration
== Synopsis
cockpit-desktop URLPATH [SSH_HOST]
== Description
The cockpit-desktop program provides secure access to Cockpit pages
in an already running desktop session. It starts a web server
(cockpit-ws) and a web browser in an isolated network namespace, and
a man:cockpit-bridge[1] in the running user session.
This avoids having to log into Cockpit, and having to enable
cockpit.socket system-wide. The network isolation ensures that no
other user, and not even other processes in the user's session, can
access this local web server.
URLPATH is the Cockpit page to open, i. e. the path component of
Cockpit URLs. It is highly recommended to only open a
https://docs.cockpit-project.org/cockpit-guide/latest/guide/embedding.html[particular page frame], not the entire Cockpit navigation and menu. For example, the
path +/cockpit/@localhost/storage/index.html+ will open the Storage
page. It is also possible to give abbreviated forms of urls, such as
"+/storage+" or "+/network/firewall+".
SSH_HOST is an optional SSH remote host specification (hostname
or username@hostname). If given, cockpit-bridge will be started
on the remote host through ssh(1) instead, i. e. the Cockpit web
browser will show that remote host. Note that this is more of an
experimental/demo feature.
== Environment
The BROWSER environment variable specifies the browser command (and
possibly options) that will be used to open the requested Cockpit page.
If not set, cockpit-desktop attempts to use an internal minimalistic
WebKit browser, and failing that, will attempt to detect some reasonable
alternatives.
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-ws[8], man:cockpit-bridge[1]
Doc Modules Man Pages Cockpit Tls.8 (`doc/modules/man/pages/cockpit-tls.8.adoc`)
= cockpit-tls(8)
== Name
cockpit-tls - TLS proxy for Cockpit web service
== Synopsis
cockpit-tls [--help] [--port PORT] [--no-tls] [--idle-timeout SECONDS]
== Description
The cockpit-tls program is a TLS terminating HTTP proxy for
man:cockpit-ws[8]. It manages a set of isolated cockpit-ws instances,
one per TLS client certificate, plus one for TLS without a client
certificate, and one for unencrypted HTTP. With that, one session cannot
tamper with another one through possible security vulnerability
exploits.
Users or administrators should never need to start this program as it
automatically started by man:systemd[1] via socket activation.
== Transport security
To specify the TLS certificate the web service should use, simply drop a
file with the extension .cert in the /etc/cockpit/ws-certs.d
directory, or below $XDG_CONFIG_DIRS if set (see
man:cockpit.conf[5]). If there are multiple files
in this directory, then the highest priority one is chosen after
sorting.
The .cert file should contain at least two OpenSSL style PEM blocks.
First one or more +BEGIN CERTIFICATE+ blocks for the server
certificate and intermediate certificate authorities and a second one
containing a +BEGIN PRIVATE KEY+ or similar. The key must not be
encrypted.
If there is no TLS certificate, a self-signed certificate is
automatically generated using sscg (if available) or openssl and
stored in the 0-self-signed.cert file.
When enrolling into a FreeIPA domain, an SSL certificate is requested
from the IPA server and stored in 10-ipa.cert.
To check which certificate cockpit-ws will use, run the following
command.
....
$ sudo /usr/libexec/cockpit-certificate-ensure --check
....
Or, on Debian-based systems:
....
$ sudo /usr/lib/cockpit/cockpit-certificate-ensure --check
....
If using certmonger to manage certificates, following command can be
used to generate a certificate/key pair:
....
CERT_FILE=/etc/cockpit/ws-certs.d/50-certmonger.crt
KEY_FILE=/etc/cockpit/ws-certs.d/50-certmonger.key
getcert request -f ${CERT_FILE} -k ${KEY_FILE} -D $(hostname --fqdn)
....
== Options
--help::
Show help options.
--port PORT::
Serve HTTP requests on PORT instead of port 9090. Usually Cockpit is
started on demand by systemd socket activation, and this option
has no effect. Update the ListenStream directive
cockpit.socket file in the usual systemd manner.
--no-tls::
Don't use TLS. Certificates will not be read, and https connections
denied. Then cockpit-tls will only manage a single cockpit-ws
instance, and thus not do anything different than running
cockpit-ws --no-tls directly. Only use this for debugging or
testing.
--idle-timeout SECONDS::
If greater than 0, exit if no connections have happened for the given
number of seconds, i. e. the server is idle. If not given, the default
is 90.
== Environment
The cockpit-tls program expects the RUNTIME_DIRECTORY
environment variable to be set to an empty directory (preferably in
/run/) that is only accessible by the system user under which it is
running. This contains the Unix sockets for communicating with the
cockpit-ws instances, and in the future, state information about
client certificates. This variable is normally set by the
cockpit.service systemd unit.
In addition, cockpit-tls will use the XDG_CONFIG_DIRS
environment variable from the
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
basedir spec] to find its certificates and the man:cockpit.conf[5]
configuration file.
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-ws[8] , man:cockpit.conf[5] , man:systemd[1]
Doc Modules Man Pages Cockpit Ws.8 (`doc/modules/man/pages/cockpit-ws.8.adoc`)
= cockpit-ws(8)
== Name
cockpit-ws - Cockpit web service
== Synopsis
cockpit-ws [--help] [--port PORT] [--address ADDRESS] [--no-tls]
[--for-tls-proxy] [--local-ssh] [--local-session BRIDGE]
== Description
The cockpit-ws program is the web service component used for
communication between the browser application and various configuration
tools and services like man:cockpit-bridge[1].
Users or administrators should never need to start this program as it
automatically started by man:systemd[1] on boot up, through
man:cockpit-tls[8].
== Transport security
cockpit-ws is normally run behind the cockpit-tls TLS
terminating proxy, and only deals with unencrypted HTTP by itself. But
for backwards compatibility it can also handle TLS connections by itself
when being run directly. For details how to configure certificates,
please refer to the man:cockpit-tls[8] documentation.
== Timeout
When started via man:systemd[1] then cockpit-ws will exit after 90
seconds if nobody logs in, or after the last user is disconnected.
== Options
*--help*:: Show help options. *--port* _PORT_:: Serve HTTP requests _PORT_ instead of port *9090*. Usually Cockpit is started on demand by *systemd* socket activation, and this option has no effect. Update the *ListenStream* directive *cockpit.socket* file in the usual *systemd* manner. *--address* _ADDRESS_:: Bind to address _ADDRESS_ instead of binding to all available addresses. Usually Cockpit is started on demand by *systemd* socket activation, and this option has no effect. In that case, update the *ListenStream* directive in the *cockpit.socket* file in the usual *systemd* manner. *--no-tls*:: Disable http to https redirection. *--for-tls-proxy*:: Tell *cockpit-ws* that it is running behind a local reverse proxy that does the TLS termination. Then Cockpit puts https:// URLs into the default *Content-Security-Policy*, and accepts only https:// origins, instead of http: ones by default. However, if *Origins* is set in the man:cockpit.conf[5] configuration file, it will override this default. *--local-ssh*:: Normally *cockpit-ws* uses *cockpit-session* and PAM to authenticate the user and start a user session. With this option enabled, it will instead authenticate via SSH at *127.0.0.1* port *22*. *--local-session* _BRIDGE_:: Skip all authentication and *cockpit-session*, and launch the *cockpit-bridge* specified in _BRIDGE_ in the local session. If the _BRIDGE_ is specified as `+-+` then expect an already running bridge that is connected to stdin and stdout of this *cockpit-ws* process. This allows the web server to run as any unprivileged user in an already running session. + + This mode implies *--no-tls*, thus you need to use http:// URLs with this. [WARNING]
With *--local-session* _BRIDGE_, you _have to isolate the opened TCP port_ somehow (for example in a network namespace), otherwise all other users (or even remote machines if the port is not just listening on localhost) can access the session!
== Environment
The cockpit-ws process will use the XDG_CONFIG_DIRS environment
variable from the
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
basedir spec] to find its man:cockpit.conf[5] configuration file.
In addition the XDG_DATA_DIRS environment variable from the
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
basedir spec] can be used to override the location to serve static files
from. These are the files that are served to a non-logged in user.
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-tls[8] , man:cockpit.conf[5] , man:systemd[1]
Doc Modules Man Pages Cockpit.1 (`doc/modules/man/pages/cockpit.1.adoc`)
= cockpit(1)
== Name
cockpit - Cockpit
== Description
Cockpit is a web accessible interactive admin interface for Linux
machines. Cockpit can usually be accessed on port 9090 of the
machine it's installed on. Cockpit starts on demand. Use your system
credentials to log in.
== Components
The cockpit-ws web service listens on port 9090 and is started
on demand by systemd. The Cockpit web service authenticates the
user, loads Cockpit into the browser, and starts cockpit-bridge in a
Linux user session.
The cockpit-bridge provides Cockpit in the web browser with access
to the system APIs. It does this over its standard in and standard out.
The bridge is started like a shell once per Linux user session.
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-tls[8] , man:cockpit-bridge[1] , man:systemd[1], https://docs.cockpit-project.org/cockpit-guide/latest[Cockpit guide]
Doc Modules Man Pages Cockpit.Conf.5 (`doc/modules/man/pages/cockpit.conf.5.adoc`)
= cockpit.conf(5)
== Name
cockpit.conf - Cockpit configuration file
== Description
Cockpit can be configured via /etc/cockpit/cockpit.conf. If
$XDG_CONFIG_DIRS is set, then the first path containing a
../cockpit/cockpit.conf is used instead. Other configuration files
and directories are searched for in the same way.
This file is not required and may need to be created manually. The file
has a INI file syntax and thus contains key / value pairs, grouped into
topical groups. See the examples below for details.
Note: The port that cockpit listens on cannot be changed in this file.
To change the port change the systemd cockpit.socket file.
== WebService
*Origins*:: By default cockpit will not accept crossdomain websocket connections. Use this setting to allow access from alternate domains. Origins should include scheme, host and port, if necessary. Wildcards and glob expressions are permitted. IPv6 addresses must have their brackets escaped with backslashes (e.g. `+\[::1\]+`) as they are matched using `+fnmatch()+`. + [source,ini]
[WebService] Origins = https://somedomain1.com https://somedomain2.com:9090 https://*.somedomain3.com https://\[::1\]:9090
*ProtocolHeader*:: Configure cockpit to look at the contents of this header to determine if a connection is using tls. This should only be used when cockpit is behind a reverse proxy, and care should be taken to make sure that incoming requests cannot set this header. + [source,ini]
[WebService] ProtocolHeader = X-Forwarded-Proto
*ForwardedForHeader*:: Configure cockpit to look at the contents of this header to determine the real origin of a connection. This should only be used when cockpit is behind a reverse proxy, and care should be taken to make sure that incoming requests cannot set this header. + [source,ini]
[WebService] ForwardedForHeader = X-Forwarded-For
LoginTitle::
Set the browser title for the login screen.
LoginTo::
When set to +true+ the Connect to option on the login screen is
visible and allows logging into another server. When set to +false+,
direct remote logins are disallowed. If this option is not specified
then it will be automatically detected based on whether the
cockpit-bridge package is installed and the ssh program is
available. +
+
If cockpit-ws is exposed to the public internet, and also has access
to a private internal network, it is recommended to explicitly set
LoginTo=false. This prevents unauthenticated remote attackers from
scanning the internal network for existing machines and open ports.
RequireHost::
When set to +true+ cockpit will require users to use the Connect
to option to specify the host to log into.
AllowMultiHost::
When set to +true+, cockpit will allow users to connect to multiple
hosts in one session. The default is OS specific. +
+
When connecting to multiple servers, JavaScript runs without
isolation. All systems will be vulnerable to potential attacks from
other connected hosts. Enable this option only when all hosts are
trusted.
MaxStartups::
Same as the sshd configuration option by the same name. Specifies
the maximum number of concurrent login attempts allowed. Additional
connections will be dropped until authentication succeeds or the
connections are closed. Defaults to 10. +
+
Alternatively, random early drop can be enabled by specifying the
three colon separated values start:rate:full (e.g. "10:30:60").
Cockpit will start refusing authentication attempts with a probability
of rate/100 (30%) if there are currently start (10)
unauthenticated connections. The probability increases linearly and
all connection attempts are refused if the number of unauthenticated
connections reaches full (60).
MaxConcurrentConnections::
Limits the number of concurrent HTTP connections on the Cockpit port.
Connection attempts that exceed this limit are rejected early on
without reading a single byte. This can be used during brute-force
attacks. It differs from MaxStartups, which protects against too
many concurrent authentication attempts. Defaults to 500.
AllowUnencrypted::
If true, cockpit will accept unencrypted HTTP connections. Otherwise,
it redirects all HTTP connections to HTTPS. Exceptions are connections
from localhost and for certain URLs (like /ping). Defaults to
false.
UrlRoot::
The root URL where you will be serving cockpit. When provided cockpit
will expect all requests to be prefixed with the given url. This is
mostly useful when you are using cockpit behind a reverse proxy, such
as nginx. +/cockpit/+ and +/cockpit++ are reserved and should not
be used. For example +/cockpit-new/+ is ok. +/cockpit/+ and
+/cockpit+new/+ are not.
ClientCertAuthentication::
If true, enable TLS client certificates for authenticating users.
Commonly these are provided by a smart card, but it's equally possible
to import certificates directly into the web browser. Please see the
https://docs.cockpit-project.org/cockpit-guide/latest/guide/cert-authentication.html[Certificate/smart
card authentication] section in the Cockpit guide for details.
Shell::
The relative URL to top level component to display in Cockpit once
logged in. Defaults to +/shell/index.html+
CustomLoginPage::
Load the login page from an alternative directory. The directory must
contain a +login.html+ and optionally a +po.js+ file for translations.
When not set, the hardcoded +$prefix/share/cockpit/static+ directory is used.
== Log
Fatal::
The kind of log messages in the bridge to treat as fatal. Separate
multiple values with spaces. Relevant values are: criticals and
warnings.
== OAuth
Cockpit can be configured to support the
https://tools.ietf.org/html/rfc6749#section-4.2[implicit grant] OAuth
authorization flow. When successful the resulting oauth token will be
passed to cockpit-ws using the Bearer auth-scheme. For a login to be
successful, cockpit will also need a to be configured to verify and
allow Bearer tokens.
URL::
This is the url that cockpit will redirect the users browser to when
it needs to obtain an oauth token. Cockpit will add a redirect_uri
parameter to the url with the location of where the oauth provider
should redirect to once a token has been obtained.
ErrorParam::
When a oauth provider redirects a user back to cockpit, look for this
parameter in the querystring or fragment portion of the url to find a
error message. When not provided it will default to
error_description
TokenParam::
When a oauth provider redirects a user back to cockpit, look for this
parameter in the querystring or fragment portion of the url to find
the access token. When not provided it will default to
access_token
== Session
*Banner*:: The contents of the specified file (commonly */etc/issue*) are shown on the login page. By default, no banner is displayed. *IdleTimeout*:: Time in minutes after which session expires and user is logged out if no user action has been performed in the given time. This idle timeout only applies to interactive password logins. With non-interactive authentication methods like Kerberos, OAuth, or certificate login, the browser cannot forget credentials, and thus automatic logouts are not useful for protecting credentials of forgotten sessions. Set to *0* to disable session timeout. + [source,ini]
[Session] IdleTimeout=15
{empty}::
When not specified, there is no idle timeout by default.
*WarnBeforeConnecting*:: Whether to warn before connecting to remote hosts from the Shell. Defaults to true. + [source,ini]
[Session] WarnBeforeConnecting=false
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
== See also
man:cockpit-ws[8], man:cockpit-tls[8]
Doc Modules Man Pages Pam Ssh Add.8 (`doc/modules/man/pages/pam_ssh_add.8.adoc`)
= pam_ssh_add(8)
== Name
pam_ssh_add - PAM module to auto load ssh keys into an agent
== Description
pam_ssh_add provides authentication and session modules that allow users
to start their session with a running ssh-agent with as many ssh keys
loaded as possible.
If used, the authentication module simply stores the authentication
token for later use by the session module. Because this module performs
no actual authentication it returns PAM_CRED_INSUFFICIENT on success and
should always be accompanied by an actual authentication module in your
pam configuration.
By default the session module will start a new ssh-agent and run
ssh-add, loading any keys that exist in the default path for the newly
logged in user. If any keys prompt for a password, and a authentication
token was successfully stored, that token will be provided as the
password.
== Options
debug::
This option will turn on debug logging to syslog.
== Examples
....
auth required pam_unix.so
auth optional pam_ssh_add.so
session optional pam_ssh_add.so
....
== Bugs
include::../partials/bugs.adoc[]
== Author
include::../partials/author.adoc[]
Doc Modules Man Partials Author (`doc/modules/man/partials/author.adoc`)
Cockpit has been written by many
https://github.com/cockpit-project/cockpit[contributors].
Doc Modules Man Partials Bugs (`doc/modules/man/partials/bugs.adoc`)
Please send bug reports to either the distribution bug tracker or the https://github.com/cockpit-project/cockpit/issues/new[upstream bug tracker].
--- METRICS ---
- Files Extracted: 82
- Estimated Token Budget: ~85218 tokens
- Canonical Reference: https://codewiki.google/github.com/cockpit-project/cockpit