diff --git a/doc/guide/asciidoc/Makefile b/doc/guide/asciidoc/Makefile new file mode 100644 index 000000000000..0edf8345bb72 --- /dev/null +++ b/doc/guide/asciidoc/Makefile @@ -0,0 +1,12 @@ +all: render_html render_man + +render_html: + mkdir -p output/html + asciidoctor -b html5 -D output/html cockpit-guide.adoc + +render_man: + mkdir -p output/html + asciidoctor -b manpage -D output/man ./man/*.adoc + +clean: + rm -rf output/ diff --git a/doc/guide/asciidoc/api-base1.adoc b/doc/guide/asciidoc/api-base1.adoc new file mode 100644 index 000000000000..b332bbe2c86b --- /dev/null +++ b/doc/guide/asciidoc/api-base1.adoc @@ -0,0 +1 @@ +This package contains basic support API available to other packages. diff --git a/doc/guide/asciidoc/api-cockpit.adoc b/doc/guide/asciidoc/api-cockpit.adoc new file mode 100644 index 000000000000..3d61bde3cc52 --- /dev/null +++ b/doc/guide/asciidoc/api-cockpit.adoc @@ -0,0 +1,14 @@ +cockpit.js + +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. + +.... + +.... + +Do not assume you can link to any file in any other package. Refer to +the link:#development[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. + +* link:#development[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 link:#cockpit-bridge.1[`cockpit-bridge`] 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 + } + } +} +.... diff --git a/doc/guide/asciidoc/privileges.adoc b/doc/guide/asciidoc/privileges.adoc new file mode 100644 index 000000000000..cbc6205cb96a --- /dev/null +++ b/doc/guide/asciidoc/privileges.adoc @@ -0,0 +1,58 @@ +[[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"]; +}); +.... diff --git a/doc/guide/asciidoc/sso.adoc b/doc/guide/asciidoc/sso.adoc new file mode 100644 index 000000000000..73f145cb1c4b --- /dev/null +++ b/doc/guide/asciidoc/sso.adoc @@ -0,0 +1,179 @@ +[[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/server.example.com@EXAMPLE.COM` +IPA and MIT:: + `HTTP/server.example.com@EXAMPLE.COM` + +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/server.example.com@EXAMPLE.COM +# or, if it already exists, just enable delegation: +$ sudo ipa service-mod --ok-as-delegate=true --ok-to-auth-as-delegate=true \ + HTTP/server.example.com@EXAMPLE.COM +.... + +Then generate a key for that principal: + +.... +$ sudo ipa-getkeytab -p HTTP/server.example.com@EXAMPLE.COM -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 user@example.com +user@example.com:*: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 `admins@example.com` 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 user@EXAMPLE.COM +Password for user@EXAMPLE.COM: +.... + +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: + + +.... +{ + "AuthServerWhitelist": "*example.com" +} +.... + + + 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 user@EXAMPLE.COM +Password for user@EXAMPLE.COM: +.... + +Make sure that the forwardable flag `F` is present in your ticket: + +.... +$ klist -f +Ticket cache: KEYRING:persistent:1000:1000 +Default principal: user@EXAMPLE.COM + +Valid starting Expires Service principal +18.03.2017 05:39:23 19.03.2017 05:39:20 krbtgt/EXAMPLE.COM@EXAMPLE.COM + 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: + + +.... +{ + "AuthServerWhitelist": "*example.com", + "AuthNegotiateDelegateWhitelist": "*example.com" +} +.... + + + 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` diff --git a/doc/guide/asciidoc/startup.adoc b/doc/guide/asciidoc/startup.adoc new file mode 100644 index 000000000000..3b530952dc37 --- /dev/null +++ b/doc/guide/asciidoc/startup.adoc @@ -0,0 +1,41 @@ +[[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`, +link:#listen[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 +.... diff --git a/doc/guide/asciidoc/urls.adoc b/doc/guide/asciidoc/urls.adoc new file mode 100644 index 000000000000..f0639efb449f --- /dev/null +++ b/doc/guide/asciidoc/urls.adoc @@ -0,0 +1,68 @@ +[[urls]] +== Cockpit URLs + +Cockpit URLs follow a specific structure, related to the components they +are loading. Various components are loaded in `