Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove password authentication #62

Merged
merged 17 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions client/incus_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import (
"github.com/lxc/incus/shared/api"
)

// GetCluster returns information about a cluster
//
// If this client is not trusted, the password must be supplied.
// GetCluster returns information about a cluster.
func (r *ProtocolIncus) GetCluster() (*api.Cluster, string, error) {
if !r.HasExtension("clustering") {
return nil, "", fmt.Errorf("The server is missing the required \"clustering\" API extension")
Expand All @@ -29,7 +27,7 @@ func (r *ProtocolIncus) UpdateCluster(cluster api.ClusterPut, ETag string) (Oper
return nil, fmt.Errorf("The server is missing the required \"clustering\" API extension")
}

if cluster.ServerAddress != "" || cluster.ClusterPassword != "" || len(cluster.MemberConfig) > 0 {
if cluster.ServerAddress != "" || cluster.ClusterToken != "" || len(cluster.MemberConfig) > 0 {
if !r.HasExtension("clustering_join") {
return nil, fmt.Errorf("The server is missing the required \"clustering_join\" API extension")
}
Expand Down
39 changes: 4 additions & 35 deletions cmd/incus-migrate/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import (
"strings"

"golang.org/x/sys/unix"
"golang.org/x/term"

"github.com/lxc/incus/client"
"github.com/lxc/incus/incusd/migration"
"github.com/lxc/incus/shared"
"github.com/lxc/incus/shared/api"
cli "github.com/lxc/incus/shared/cmd"
"github.com/lxc/incus/shared/version"
"github.com/lxc/incus/shared/ws"
)
Expand Down Expand Up @@ -230,51 +228,22 @@ func connectTarget(url string, certPath string, keyPath string, authType string,
if authType == "tls" {
if token != "" {
req := api.CertificatesPost{
Password: token,
TrustToken: token,
}

err = c.CreateCertificate(req)
if err != nil {
return nil, "", fmt.Errorf("Failed to create certificate: %w", err)
}
} else {
fmt.Println("It is recommended to have this certificate be manually added to Incus through `incus config trust add` on the target server.\nAlternatively you could use a pre-defined trust password to add it remotely (use of a trust password can be a security issue).")

fmt.Println("A temporary client certificate was generated, use `incus config trust add` on the target server.")
fmt.Println("")

useTrustPassword, err := cli.AskBool("Would you like to use a trust password? [default=no]: ", "no")
fmt.Print("Press ENTER after the certificate was added to the remote server: ")
_, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return nil, "", err
}

if useTrustPassword {
// Prompt for trust password
fmt.Print("Trust password: ")
pwd, err := term.ReadPassword(0)
if err != nil {
return nil, "", err
}

fmt.Println("")

// Add client certificate to trust store
req := api.CertificatesPost{
Password: string(pwd),
}

req.Type = api.CertificateTypeClient

err = c.CreateCertificate(req)
if err != nil {
return nil, "", err
}
} else {
fmt.Print("Press ENTER after the certificate was added to the remote server: ")
_, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return nil, "", err
}
}
}
} else {
c.RequireAuthenticated(true)
Expand Down
5 changes: 1 addition & 4 deletions cmd/incus/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,7 @@ For backward compatibility, a single configuration key may still be set with:
Will set a CPU limit of "2" for the instance.

incus config set core.https_address=[::]:8443
Will have the server listen on IPv4 and IPv6 port 8443.

incus config set core.trust_password=blah
Will set the server's trust password to blah.`))
Will have the server listen on IPv4 and IPv6 port 8443.`))

cmd.Flags().StringVar(&c.config.flagTarget, "target", "", i18n.G("Cluster member name")+"``")
cmd.Flags().BoolVarP(&c.flagIsProperty, "property", "p", false, i18n.G("Set the key as an instance property"))
Expand Down
25 changes: 8 additions & 17 deletions cmd/incus/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"strings"

"github.com/spf13/cobra"
"golang.org/x/term"

"github.com/lxc/incus/client"
config "github.com/lxc/incus/internal/cliconfig"
Expand Down Expand Up @@ -73,7 +72,7 @@ type cmdRemoteAdd struct {
remote *cmdRemote

flagAcceptCert bool
flagPassword string
flagToken string
flagPublic bool
flagProtocol string
flagAuthType string
Expand All @@ -95,7 +94,7 @@ Basic authentication can be used when combined with the "simplestreams" protocol

cmd.RunE = c.Run
cmd.Flags().BoolVar(&c.flagAcceptCert, "accept-certificate", false, i18n.G("Accept certificate"))
cmd.Flags().StringVar(&c.flagPassword, "password", "", i18n.G("Remote admin password")+"``")
cmd.Flags().StringVar(&c.flagToken, "token", "", i18n.G("Remote trust token")+"``")
cmd.Flags().StringVar(&c.flagProtocol, "protocol", "", i18n.G("Server protocol (incus or simplestreams)")+"``")
cmd.Flags().StringVar(&c.flagAuthType, "auth-type", "", i18n.G("Server authentication type (tls or oidc)")+"``")
cmd.Flags().BoolVar(&c.flagPublic, "public", false, i18n.G("Public image server"))
Expand Down Expand Up @@ -244,7 +243,7 @@ func (c *cmdRemoteAdd) addRemoteFromToken(addr string, server string, token stri
}

req := api.CertificatesPost{
Password: token,
TrustToken: token,
}

err = d.CreateCertificate(req)
Expand Down Expand Up @@ -548,25 +547,17 @@ func (c *cmdRemoteAdd) Run(cmd *cobra.Command, args []string) error {
// Check if additional authentication is required.
if srv.Auth != "trusted" {
if c.flagAuthType == "tls" {
// Prompt for trust password
if c.flagPassword == "" {
fmt.Printf(i18n.G("Admin password (or token) for %s:")+" ", server)
pwd, err := term.ReadPassword(0)
// Prompt for trust token
if c.flagToken == "" {
c.flagToken, err = cli.AskString(fmt.Sprintf(i18n.G("Trust token for %s: "), server), "", nil)
if err != nil {
/* We got an error, maybe this isn't a terminal, let's try to
* read it as a file */
pwd, err = shared.ReadStdin()
if err != nil {
return err
}
return err
}
fmt.Println("")
c.flagPassword = string(pwd)
}

// Add client certificate to trust store
req := api.CertificatesPost{
Password: c.flagPassword,
TrustToken: c.flagToken,
}

req.Type = api.CertificateTypeClient
Expand Down
9 changes: 0 additions & 9 deletions doc/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ You can obtain the list of TLS certificates trusted by a LXD server with `lxc co
Trusted clients can be added in either of the following ways:

- {ref}`authentication-add-certs`
- {ref}`authentication-trust-pw`
- {ref}`authentication-token`

The workflow to authenticate with the server is similar to that of SSH, where an initial connection to an unknown server triggers a prompt:
Expand Down Expand Up @@ -79,14 +78,6 @@ If the list of projects is empty, the client will not be allowed access to any o
The preferred way to add trusted clients is to directly add their certificates to the trust store on the server.
To do so, copy the client certificate to the server and register it using `lxc config trust add <file>`.

(authentication-trust-pw)=
#### Adding client certificates using a trust password

To allow establishing a new trust relationship from the client side, you must set a trust password ([`core.trust_password`](server-options-core)) for the server. Clients can then add their own certificate to the server's trust store by providing the trust password when prompted.

In a production setup, unset `core.trust_password` after all clients have been added.
This prevents brute-force attacks trying to guess the password.

(authentication-token)=
#### Adding client certificates using tokens

Expand Down
5 changes: 2 additions & 3 deletions doc/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ By default, the LXD server is not accessible from the network, because it only l

You can enable it for remote access by following the instructions in {ref}`server-expose`.

## When I do a `lxc remote add`, it asks for a password or token?
## When I do a `lxc remote add`, it asks for a token?

To be able to access the remote API, clients must authenticate with the LXD server.
Depending on how the remote server is configured, you must provide either a trust token issued by the server or specify a trust password (if [`core.trust_password`](server-options-core) is set).

See {ref}`server-authenticate` for instructions on how to authenticate using a trust token (the recommended way), and {doc}`authentication` for information about other authentication methods.
See {ref}`server-authenticate` for instructions on how to authenticate using a trust token.

## Why should I not run privileged containers?

Expand Down
106 changes: 0 additions & 106 deletions doc/howto/cluster_form.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ You can accept the default values for most questions, but make sure to answer th
- `Are you joining an existing cluster?`

Select **no**.
- `Setup password authentication on the cluster?`

Select **no** to use {ref}`authentication tokens <authentication-token>` (recommended) or **yes** to use a {ref}`trust password <authentication-trust-pw>`.

<details>
<summary>Expand to see a full example for <code>lxd init</code> on the bootstrap server</summary>
Expand Down Expand Up @@ -97,9 +94,7 @@ Basically, the initialization process consists of the following steps:
- `Are you joining an existing cluster?`

Select **yes**.
- `Do you have a join token?`

Select **yes** if you configured the bootstrap server to use {ref}`authentication tokens <authentication-token>` (recommended) or **no** if you configured it to use a {ref}`trust password <authentication-trust-pw>`.
1. Authenticate with the cluster.

There are two alternative methods, depending on which authentication method you choose when configuring the bootstrap server.
Expand All @@ -118,14 +113,6 @@ Basically, the initialization process consists of the following steps:
The join token contains the addresses of the existing online members, as well as a single-use secret and the fingerprint of the cluster certificate.
This reduces the amount of questions that you must answer during `lxd init`, because the join token can be used to answer these questions automatically.
````
````{group-tab} Trust password
If you configured your cluster to use a {ref}`trust password <authentication-trust-pw>`, `lxd init` requires more information about the cluster before it can start the authorization process:

1. Specify a name for the new cluster member.
1. Provide the address of an existing cluster member (the bootstrap server or any other server you have already added).
1. Verify the fingerprint for the cluster.
1. If the fingerprint is correct, enter the trust password to authorize with the cluster.
````

`````

Expand Down Expand Up @@ -201,8 +188,6 @@ You need a different preseed file for every server.

### Initialize the bootstrap server

The required contents of the preseed file depend on whether you want to use {ref}`authentication tokens <authentication-token>` (recommended) or a {ref}`trust password <authentication-trust-pw>` for authentication.

`````{tabs}

````{group-tab} Authentication tokens (recommended)
Expand Down Expand Up @@ -246,55 +231,11 @@ cluster:
```

````
````{group-tab} Trust password
To enable clustering, the preseed file for the bootstrap server must contain the following fields:

```yaml
config:
core.https_address: <IP_address_and_port>
core.trust_password: <trust_password>
cluster:
server_name: <server_name>
enabled: true
```

Here is an example preseed file for the bootstrap server:

```yaml
config:
core.trust_password: the_password
core.https_address: 192.0.2.101:8443
images.auto_update_interval: 15
storage_pools:
- name: default
driver: dir
networks:
- name: lxdbr0
type: bridge
profiles:
- name: default
devices:
root:
path: /
pool: default
type: disk
eth0:
name: eth0
nictype: bridged
parent: lxdbr0
type: nic
cluster:
server_name: server1
enabled: true
```

````
`````

### Join additional servers

The required contents of the preseed files depend on whether you configured the bootstrap server to use {ref}`authentication tokens <authentication-token>` (recommended) or a {ref}`trust password <authentication-trust-pw>` for authentication.

The preseed files for new cluster members require only a `cluster` section with data and configuration values that are specific to the joining server.

`````{tabs}
Expand Down Expand Up @@ -324,54 +265,7 @@ cluster:
```

````
````{group-tab} Trust password
The preseed file for additional servers must include the following fields:

```yaml
cluster:
server_name: <server_name>
enabled: true
cluster_address: <IP_address_of_bootstrap_server>
server_address: <IP_address_of_server>
cluster_password: <trust_password>
cluster_certificate: <certificate> # use this or cluster_certificate_path
cluster_certificate_path: <path_to-certificate_file> # use this or cluster_certificate
```

To create a YAML-compatible entry for the `cluster_certificate` key, run one the following commands on the bootstrap server:

- When using the snap: `sed ':a;N;$!ba;s/\n/\n\n/g' /var/snap/lxd/common/lxd/cluster.crt`
- Otherwise: `sed ':a;N;$!ba;s/\n/\n\n/g' /var/lib/lxd/cluster.crt`

Alternatively, copy the `cluster.crt` file from the bootstrap server to the server that you want to join and specify its path in the `cluster_certificate_path` key.

Here is an example preseed file for a new cluster member:

```yaml
cluster:
server_name: server2
enabled: true
server_address: 192.0.2.102:8443
cluster_address: 192.0.2.101:8443
cluster_certificate: "-----BEGIN CERTIFICATE-----

opyQ1VRpAg2sV2C4W8irbNqeUsTeZZxhLqp4vNOXXBBrSqUCdPu1JXADV0kavg1l

2sXYoMobyV3K+RaJgsr1OiHjacGiGCQT3YyNGGY/n5zgT/8xI0Dquvja0bNkaf6f

...

-----END CERTIFICATE-----
"
cluster_password: the_password
member_config:
- entity: storage-pool
name: default
key: source
value: ""
```

````
`````

## Use MicroCloud
Expand Down
1 change: 0 additions & 1 deletion doc/howto/initialize.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ You can use it as a template for your own preseed file and add, change or remove
# Daemon settings
config:
core.https_address: 192.0.2.1:9999
core.trust_password: sekret
images.auto_update_interval: 6

# Storage pools
Expand Down
Loading
Loading