//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

=== Security

Apache Karaf provides an advanced and flexible security system, powered by JAAS (Java Authentication and Authorization
Service) in an OSGi compliant way.

It provides a dynamic security system.

The Apache Karaf security framework is used internally to control the access to:

* the OSGi services (described in the developer guide)
* the console commands
* the JMX layer
* the WebConsole

Your applications can also use the security framework (see the developer guide for details).

[NOTE]
====
For security reason, by default, `karaf` user is disabled. To allow the logon, you have to have an user. You can enable
`karaf` user by uncommenting the user in `etc/users.properties`:

----
karaf = karaf,_g_:admingroup
_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
----
====

==== Realms

Apache Karaf is able to manage multiple realms. A realm contains the definition of the login modules to use for the
authentication and/or authorization on this realm. The login modules define the authentication and authorization for
the realm.

The `jaas:realm-list` command lists the current defined realms:

----
karaf@root()> jaas:realm-list
Index | Realm Name | Login Module Class Name
-----------------------------------------------------------------------------------
1     | karaf      | org.apache.karaf.jaas.modules.properties.PropertiesLoginModule
2     | karaf      | org.apache.karaf.jaas.modules.publickey.PublickeyLoginModule
----

You can see that the Apache Karaf provides a default realm named `karaf`.

This realm has two login modules:

* the `PropertiesLoginModule` uses the `etc/users.properties` file as backend for users, groups, roles and password.
 This login module authenticates the users and returns the users' roles.
* the `PublickeyLoginModule` is especially used by the SSHd. It uses the `etc/keys.properties` file. This file contains
 the users and a public key associated to each user.

Apache Karaf provides additional login modules (see the developer guide for details):

* JDBCLoginModule uses a database as backend
* LDAPLoginModule uses a LDAP server as backend
* SyncopeLoginModule uses Apache Syncope as backend
* OsgiConfigLoginModule uses a configuration as backend
* Krb5LoginModule uses a Kerberos Server as backend
* GSSAPILdapLoginModule uses a LDAP server as backend but delegate LDAP server authentication to an other backend (typically Krb5LoginModule)

You can manage an existing realm, login module, or create your own realm using the `jaas:realm-manage` command.

===== Adding Realms or Login Modules

If a new realm should be added Apache Karaf provides an easy way to create a new Realm (although with limited flexibility compared to other approaches like blueprint or directly via DS).
The `jaas:realm-add` command can be used for that purpose.
Note, that it takes at least 2 Parameters (name of the realm and class name of the initial Login Module) or even more, if the Login Module needs parameters.
For example, a new realm `myrealm` which uses the `PropertiesLoginModule` with a `users` file located in `/tmp/users` can be added by the command

----
jaas:realm-add myrealm org.apache.karaf.jaas.modules.properties.PropertiesLoginModule users "/tmp/users"
----

To add a new Login Module to an existing realm the `jaas:module-add` command can be used. It is similar in semantics than the `jaas:realm-add` command except that it takes no realm name.
Instead, the realm to modify has to be selected via `jaas:realm-manage` previously.
Note that the options for the Login Module are given as a list of parameters which is interpreted as a map in the order
"key1 value1 key2 value2".
So the command

----
jaas:module-add org.apache.karaf.jaas.modules.properties.PropertiesLoginModule users "/tmp/users"
----

will add a `PropertiesLoginModule` with parameters map `users -> /tmp/users` to the selected realm.

==== Users, groups, roles, and passwords

As we saw, by default, Apache Karaf uses a PropertiesLoginModule.

This login module uses the `etc/users.properties` file as storage for the users, groups, roles and passwords.

The initial `etc/users.properties` file contains:

----
################################################################################
#
#    Licensed to the Apache Software Foundation (ASF) under one or more
#    contributor license agreements.  See the NOTICE file distributed with
#    this work for additional information regarding copyright ownership.
#    The ASF licenses this file to You under the Apache License, Version 2.0
#    (the "License"); you may not use this file except in compliance with
#    the License.  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#
################################################################################

#
# This file contains the users, groups, and roles.
# Each line has to be of the format:
#
# USER=PASSWORD,ROLE1,ROLE2,...
# USER=PASSWORD,_g_:GROUP,...
# _g_\:GROUP=ROLE1,ROLE2,...
#
# All users, grousp, and roles entered in this file are available after Karaf startup
# and modifiable via the JAAS command group. These users reside in a JAAS domain
# with the name "karaf".
#
karaf = karaf,_g_:admingroup
_g_\:admingroup = group,admin,manager,viewer,ssh
----

We can see in this file, that we have one user by default: `karaf`.
The default password is `karaf`.

The `karaf` user is member of one group: the `admingroup`.

A group is always prefixed by `_g_:`. An entry without this prefix is a user.

A group defines a set of roles. By default, the `admingroup` defines `group`, `admin`, `manager`, and `viewer`
roles.

It means that the `karaf` user will have the roles defined by the `admingroup`.

===== Commands

The `jaas:*` commands manage the realms, users, groups, roles in the console.

====== `jaas:realm-list`

We already used the `jaas:realm-list` previously in this section.

The `jaas:realm-list` command lists the realm and the login modules for each realm:

----
karaf@root()> jaas:realm-list
Index | Realm Name | Login Module Class Name
-----------------------------------------------------------------------------------
1     | karaf      | org.apache.karaf.jaas.modules.properties.PropertiesLoginModule
2     | karaf      | org.apache.karaf.jaas.modules.publickey.PublickeyLoginModule
----

We have here one realm (`karaf`) containing two login modules (`PropertiesLoginModule` and `PublickeyLoginModule`).

The `index` is used by the `jaas:realm-manage` command to easily identify the realm/login module that we want to manage.

====== `jaas:realm-manage`

The `jaas:realm-manage` command switch in realm/login module edit mode, where you can manage the users, groups, and roles in the login module.

To identify the realm and login module that you want to manage, you can use the `--index` option.
The indexes are displayed by the `jaas:realm-list` command:

----
karaf@root()> jaas:realm-manage --index 1
----

Another way is to use the `--realm` and `--module` options. The `--realm` option expects the realm name, and the `--module`
option expects the login module class name:

----
karaf@root()> jaas:realm-manage --realm karaf --module org.apache.karaf.jaas.modules.properties.PropertiesLoginModule
----

====== `jaas:user-list`

When you are in edit mode, you can list the users in the login module using the `jaas:user-list`:

----
karaf@root()> jaas:user-list
User Name | Group      | Role
--------------------------------
karaf     | admingroup | admin
karaf     | admingroup | manager
karaf     | admingroup | viewer
----

You can see the user name and the group by role.

====== `jaas:user-add`

The `jaas:user-add` command adds a new user (and the password) in the currently edited login module:

----
karaf@root()> jaas:user-add foo bar
----

To "commit" your change (here the user addition), you have to execute the `jaas:update` command:

----
karaf@root()> jaas:update
karaf@root()> jaas:realm-manage --index 1
karaf@root()> jaas:user-list
User Name | Group      | Role
--------------------------------
karaf     | admingroup | admin
karaf     | admingroup | manager
karaf     | admingroup | viewer
foo       |            |
----

On the other hand, if you want to rollback the user addition, you can use the `jaas:cancel` command.

====== `jaas:user-delete`

The `jaas:user-delete` command deletes a user from the currently edited login module:

----
karaf@root()> jaas:user-delete foo
----

Like for the `jaas:user-add` command, you have to use the `jaas:update` to commit your change (or `jaas:cancel` to rollback):

----
karaf@root()> jaas:update
karaf@root()> jaas:realm-manage --index 1
karaf@root()> jaas:user-list
User Name | Group      | Role
--------------------------------
karaf     | admingroup | admin
karaf     | admingroup | manager
karaf     | admingroup | viewer
----

====== `jaas:group-add`

The `jaas:group-add` command assigns a group (and eventually creates the group) to a user in the currently edited login module:

----
karaf@root()> jaas:group-add karaf mygroup
----

====== `jaas:group-delete`

The `jaas:group-delete` command removes a user from a group in the currently edited login module:

----
karaf@root()> jaas:group-delete karaf mygroup
----

====== `jaas:group-role-add`

The `jaas:group-role-add` command adds a role in a group in the currently edited login module:

----
karaf@root()> jaas:group-role-add mygroup myrole
----

====== `jaas:group-role-delete`

The `jaas:group-role-delete` command removes a role from a group in the currently edited login module:

----
karaf@root()> jaas:group-role-delete mygroup myrole
----

====== `jaas:update`

The `jaas:update` command commits your changes in the login module backend. For instance, in the case of the PropertiesLoginModule,
the `etc/users.properties` will be updated only after the execution of the `jaas:update` command.

====== `jaas:cancel`

The `jaas:cancel` command rollback your changes and doesn't update the login module backend.

==== Passwords encryption

By default, the passwords are stored in clear form in the `etc/users.properties` file.

It's possible to enable encryption in the `etc/org.apache.karaf.jaas.cfg` configuration file:

----
################################################################################
#
#    Licensed to the Apache Software Foundation (ASF) under one or more
#    contributor license agreements.  See the NOTICE file distributed with
#    this work for additional information regarding copyright ownership.
#    The ASF licenses this file to You under the Apache License, Version 2.0
#    (the "License"); you may not use this file except in compliance with
#    the License.  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#
################################################################################

#
# Boolean enabling / disabling encrypted passwords
#
encryption.enabled = false

#
# Encryption Service name
#   basic: the default encryption service which digests passwords without
#          salting them
#   jasypt: a more powerful alternative which supports salting
#   spring-security-crypto: Supports bcrypt, argon2, pbkdf2, scrypt. Requires
#                           the "spring-security-crypto-encryption" feature
#                           to be installed.
#
encryption.name = basic

#
# Encryption prefix
#
encryption.prefix = {CRYPT}

#
# Encryption suffix
#
encryption.suffix = {CRYPT}

#
# Set the encryption algorithm to use in Karaf JAAS login module
# Supported encryption algorithms follow:
#   basic/jasypt: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512
#   spring-security-crypto: argon2, bcrypt, pbkdf2, scrypt
#
encryption.algorithm = SHA-256

#
# Encoding of the encrypted password.
# Can be:
#   hexadecimal
#   base64
#
encryption.encoding = hexadecimal
----

If the `encryption.enabled` property is set to true, the password encryption is enabled.

With encryption enabled, the password are encrypted at the first time a user logs in. The encrypted passwords are
prefixed and suffixed with `\{CRYPT\`}. To re-encrypt the password, you can reset the password in clear (in `etc/users.properties`
file), without the `\{CRYPT\`} prefix and suffix. Apache Karaf will detect that this password is in clear (because it's not
prefixed and suffixed with `\{CRYPT\`}) and encrypt it again.

The `encryption.name` property defines which encryption provider is used. The default is "basic" which just supports basic
digesting of the password, without salting. This is not secure for production environments. A more secure alternative is
"jasypt", which supports digesting with salting. However, the most secure alternative which should be used in production is
"spring-security-crypto", which supports modern password digest algorithms such as "argon2" and "bcrypt".

The `etc/org.apache.karaf.jaas.cfg` configuration file allows you to define advanced encryption behaviours:

* the `encryption.prefix` property defines the prefix to "flag" a password as encrypted. The default is `\{CRYPT\`}.
* the `encryption.suffix` property defines the suffix to "flag" a password as encrypted. The default is `\{CRYPT\`}.
* the `encryption.algorithm` property defines the algorithm to use for encryption (digest). The possible values for the "basic" and
"jasypt" provides are: `MD2`, `MD5`, `SHA-1`, `SHA-256`, `SHA-384`, `SHA-512`. The default is `SHA-256` since Karaf 4.3.0, prior to this it was `MD5`. 
When the "spring-security-crypto" `encryption.name` is configured, the possible values are: `argon2`, `bcrypt`, `pbkdf2`, `scrypt`.
* the `encryption.encoding` property defines the encoding of the encrypted password. The possible values are `hexadecimal`
 or `base64`. The default value is `hexadecimal`.

==== Managing authentication by key

For the SSH layer, Karaf supports the authentication by key, allowing to login without providing the password.

The SSH client (so bin/client provided by Karaf itself, or any ssh client like OpenSSH) uses a public/private keypair that
will identify itself on Karaf SSHD (server side).

The keys allowed to connect are stored in `etc/keys.properties` file, following the format:

----
user=key,role
----

The easiest way to create key pair is to use OpenSSH.

You can create a key pair using:

----
ssh-keygen -t rsa -f karaf.id_rsa
----

NB: you can provide a passphrase with `-N` option to `ssh-keygen` but, in that case, you will need to enter the passphrase to allow the SSH client to use the key.

You have now the public and private keys:

----
-rw-------  1 jbonofre jbonofre    771 Jul 25 22:05 karaf.id_rsa
-rw-r--r--  1 jbonofre jbonofre    607 Jul 25 22:05 karaf.id_rsa.pub
----

You copy the key from `karaf.id_rsa.pub` file in the `etc/keys.properties`:

----
karaf=AAAAB3NzaC1yc2EAAAADAQABAAABAQCtXN9ZZ+K67UFbxZMxHsoR69vHNbN8qi17v/5jF83FUexRqu8FvWCInBoW7eVFyeIiFXGd/zaCrDHrZsqpwXNEha3ifvfGTY2+gMLfZZFgh2LFubXBH6G725XKs9aus+KLrwC8u/uPr9Sw3YeSb0zxrjiXqv6hGYhaAHnskAgRCUxa+P4/JYNVS/2+ZrvBrVMAwgEuwt1Y1IDYsXQRmLJPn5ayMCfzPTANXfgB7Hix    72f2XpHV3FdnKTbYwdA32Bg4ptJkuvyMXnBy5y7ChRU150YGRToC4ETcPF2DB0EPOcbOLsQlNTKKRYuNR1zEpp6RAfiWD65kmYK766CE8AbB,_g_:admingroup
_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
----

and specify to the client to use the `karaf.id_rsa` private key:

----
bin/client -k ~/karaf.id_rsa
----

or to ssh

----
ssh -p 8101 -i ~/karaf.id_rsa karaf@localhost
----

==== RBAC

Apache Karaf uses the roles to control the access to the resources: it's a RBAC (Role Based Access Control) system.

The roles are used to control:

* access to OSGi services
* access to the console (control the execution of the commands)
* access to JMX (MBeans and/or operations)
* access to the WebConsole

===== OSGi services

The details about OSGi services RBAC support is explained in the developer guide.

===== Console

Console RBAC supports is a specialization of the OSGi service RBAC. Actually, in Apache Karaf, all console commands are
defined as OSGi services.

The console command name follows the `scope:name` format.

The ACL (Access Lists) are defined in `etc/org.apache.karaf.command.acl.<scope>.cfg` configuration files, where `<scope>`
is the commands scope.

For instance, we can define the ACL to the `feature:*` commands by creating a `etc/org.apache.karaf.command.acl.feature.cfg`
configuration file. In this `etc/org.apache.karaf.command.acl.feature.cfg` configuration file, we can set:

----
list = viewer
info = viewer
install = admin
uninstall = admin
----

Here, we define that `feature:list` and `feature:info` commands can be executed by users with `viewer` role, whereas
the `feature:install` and `feature:uninstall` commands can only be executed by users with `admin` role.
Note that users in the admin group will also have viewer role, so will be able to do everything.

Apache Karaf command ACLs can control access using (inside a given command scope):

* the command name regex (e.g. `name = role`)
* the command name and options or arguments values regex (e.g. `name[/.*[0-9][0-9][0-9]+.*/] = role` to execute name only with argument value above 100)

Both command name and options/arguments support exact matching or regex matching.

By default, Apache Karaf defines the following commands ACLs:

* `etc/org.apache.karaf.command.acl.bundle.cfg` configuration file defines the ACL for `bundle:*` commands.
 This ACL limits the execution of `bundle:*` commands for system bundles only to the users with `admin` role, whereas
 `bundle:*` commands for non-system bundles can be executed by the users with `manager` role.
* `etc/org.apache.karaf.command.acl.config.cfg` configuration file defines the ACL for `config:*` commands.
 This ACL limits the execution of `config:*` commands with `jmx.acl.*`, `org.apache.karaf.command.acl.*`, and
 `org.apache.karaf.service.acl.*` configuration PID to the users with `admin` role. For the other configuration PID,
 the users with the `manager` role can execute `config:*` commands.
* `etc/org.apache.karaf.command.acl.feature.cfg` configuration file defines the ACL for `feature:*` commands.
 Only the users with `admin` role can execute `feature:install`, `feature:uninstall`,`feature:start`, `feature:stop` and `feature:update` commands. The other `feature:*`
 commands can be executed by any user.
* `etc/org.apache.karaf.command.acl.jaas.cfg` configuration file defines the ACL for `jaas:*` commands.
 Only the users with `admin` role can execute `jaas:update` command. The other `jaas:*` commands can be executed by any
 user.
* `etc/org.apache.karaf.command.acl.kar.cfg` configuration file defines the ACL for `kar:*` commands.
 Only the users with `admin` role can execute `kar:install` and `kar:uninstall` commands. The other `kar:*` commands
 can be executed by any user.
* `etc/org.apache.karaf.command.acl.shell.cfg` configuration file defines the ACL for `shell:*` and "direct" commands.
 Only the users with `admin` role can execute `shell:edit`, `shell:exec`, `shell:new`, and `shell:java` commands.
 The other `shell:*` commands can be executed by any user.
* `etc/org.apache.karaf.command.acl.system.cfg` configuration file defines the ACL for `system:*` commands.
 Only the users with `admin` role can execute `system:property` and `system:shutdown` commands. Users with `manager` role can call `system:start-level` above 100, otherwise `admin` role is required. Also users with `viewer` role can obtain the current start-level.
 The other `system:*` commands can be executed by any user.

You can change these default ACLs, and add your own ACLs for additional command scopes (for instance `etc/org.apache.karaf.command.acl.cluster.cfg` for
Apache Karaf Cellar, `etc/org.apache.karaf.command.acl.camel.cfg` from Apache Camel, ...).

You can fine tune the command RBAC support by editing the `karaf.secured.services` property in `etc/system.properties`:

----
#
# By default, only Karaf shell commands are secured, but additional services can be
# secured by expanding this filter
#
karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*))
----

===== JMX

Like for the console commands, you can define ACL (AccessLists) to the JMX layer.

The JMX ACL are defined in `etc/jmx.acl<ObjectName>.cfg` configuration file, where `<ObjectName>` is a MBean object name
(for instance `org.apache.karaf.bundle` represents `org.apache.karaf;type=Bundle` MBean).

The `etc/jmx.acl.cfg` is the most generic configuration file and is used when no specific ones are found.
It contains the "global" ACL definition.

JMX ACLs can control access using (inside a JMX MBean):

* the operation name regex (e.g. `operation* = role`)
* the operation arguments value regex (e.g. `operation(java.lang.String, int)[/([1-4])?[0-9]/,/.*/] = role`)

By default, Apache Karaf defines the following JMX ACLs:

* `etc/jmx.acl.org.apache.karaf.bundle.cfg` configuration file defines the ACL for the `org.apache.karaf:type=bundle`
 MBean. This ACL limits the `setStartLevel()`, `start()`, `stop()`, and `update()` operations for system bundles for
 only users with `admin` role. The other operations can be performed by users with the `manager` role.
* `etc/jmx.acl.org.apache.karaf.config.cfg` configuration file defines the ACL for the `org.apache.karaf:type=config`
 MBean. This ACL limits the change on `jmx.acl*`, `org.apache.karaf.command.acl*`, and `org.apache.karaf.service.acl*`
 configuration PIDs for only users with `admin` role. The other operations can be performed by users with the `manager` role.
* `etc/jmx.acl.org.apache.karaf.security.jmx.cfg` configuration file defines the ACL for the `org.apache.karaf:type=security,area=jmx`
 MBean. This ACL limits the invocation of the `canInvoke()` operation for the users with `viewer` role.
* `etc/jmx.acl.osgi.compendium.cm.cfg` configuration file defines the ACL for the `osgi.compendium:type=cm` MBean.
 This ACL limits the changes on `jmx.acl*`, `org.apache.karaf.command.acl*`, and `org.apache.karaf.service.acl*`
 configuration PIDs for only users with `admin` role. The other operations can be performed by users with the `manager` role.
* `etc/jmx.acl.java.lang.Memory.cfg` configuration file defines the ACL for the core JVM Memory MBean.
 This ACL limits the invocation of the `gc` operation for only users with the `manager` role.
* `etc/jmx.acl.cfg` configuration file is the most generic file. The ACLs defined here are used when no other specific
 ACLs match (by specific ACL, it's an ACL defined in another MBean specific `etc/jmx.acl.*.cfg` configuration file).
 The `list*()`, `get*()`, `is*()` operations can be performed by users with the `viewer` role.
 The `set*()` and all other `*()` operations can be performed by users with the `admin` role.

===== WebConsole

The Apache Karaf WebConsole is not available by default. To enable it, you have to install the `webconsole` feature:

----
karaf@root()> feature:install webconsole
----

The WebConsole doesn't support fine grained RBAC like console or JMX for now.

All users with the `admin` role can logon to the WebConsole and perform any operations.

==== SecurityMBean

Apache Karaf provides a JMX MBean to check if the current user can invoke a given MBean and/or operation.

The `canInvoke()` operation gets the roles of the current user, and check if one the roles can invoke the MBean and/or the
operation, eventually with a given argument value.

===== Operations

* `canInvoke(objectName)` returns `true` if the current user can invoke the MBean with the `objectName`, `false` else.
* `canInvoke(objectName, methodName)` returns `true` if the current user can invoke the operation `methodName` on the MBean
 with the `objectName`, `false` else.
* `canInvoke(objectName, methodName, argumentTypes)` returns `true` if the current user can invoke the operation `methodName`
with the array of arguments types `argumentTypes` on the MBean with `objectName`, `false` else.
* `canInvoke(bulkQuery)` returns a tabular data containing for each operation in the `bulkQuery` tabular data if `canInvoke`
is `true` or `false`.

==== Security providers

Some applications require specific security providers to be available, such as [BouncyCastle|http://www.bouncycastle.org].

The JVM imposes some restrictions about the use of such jars: they have to be signed and be available on the boot classpath.

One way to deploy those providers is to put them in the JRE folder at `$JAVA_HOME/jre/lib/ext` and modify the security
policy configuration (`$JAVA_HOME/jre/lib/security/java.security`) in order to register such providers.

While this approach works fine, it has a global effect and requires you to configure all your servers accordingly.

Apache Karaf offers a simple way to configure additional security providers:

* put your provider jar in `lib/ext`
* modify the `etc/config.properties` configuration file to add the following property

----
org.apache.karaf.security.providers = xxx,yyy
----

The value of this property is a comma separated list of the provider class names to register.

For instance, to add the bouncycastle security provider, you define:

----
org.apache.karaf.security.providers = org.bouncycastle.jce.provider.BouncyCastleProvider
----

In addition, you may want to provide access to the classes from those providers from the system bundle so that all bundles
can access those.

It can be done by modifying the `org.osgi.framework.bootdelegation` property in the same configuration file:

----
org.osgi.framework.bootdelegation = ...,org.bouncycastle*
----
