Skip to content

Commit

Permalink
Merge pull request #468 from PriceChild/patch-1
Browse files Browse the repository at this point in the history
Improve and document support for encrypted keys
  • Loading branch information
stgraber committed Feb 10, 2024
2 parents 15b2097 + 1d4a8f8 commit 4577279
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
2 changes: 2 additions & 0 deletions doc/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ ACLs
AIO
allocator
AMD
Ansible
Ansible's
APIs
AppArmor
ARMv
Expand Down
22 changes: 22 additions & 0 deletions doc/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,28 @@ If the server certificate is valid and signed by the CA, then the connection con

Note that the generated certificates are not automatically trusted. You must still add them to the server in one of the ways described in {ref}`authentication-trusted-clients`.

### Encrypting local keys

The `incus` client also supports encrypted client keys. Keys generated via the methods above can be encrypted with a password, using:

```
ssh-keygen -p -o -f .config/incus/client.key
```

```{note}
Unless you enable [`keepalive` mode](remote-keepalive), then every single call to Incus will cause the prompt which may get a bit annoying:
$ incus list remote-host:
Password for client.key:
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
```

```{note}
While the `incus` command line supports encrypted keys, tools such as [Ansible's connection plugin](https://docs.ansible.com/ansible/latest/collections/community/general/incus_connection.html) do not.
```

(authentication-openid)=
## OpenID Connect authentication

Expand Down
22 changes: 22 additions & 0 deletions doc/remotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,25 @@ remotes:
protocol: incus
public: false
```

(remote-keepalive)=
## Enabling `keepalive`

For those frequently interacting with a particular remote, it's possible to enable a new `keepalive` mode.

When enabled, Incus will maintain a connection with the target server for up to the configured timeout.
This can significantly reduce the latency when running many `incus` commands.

To enable, edit your `config.yml` (typically in `~/.config/incus`) and change your remote to look like:

```
my-remote:
addr: https://192.0.2.5:8443
auth_type: tls
project: default
protocol: incus
public: false
keepalive: 30
```

In this example, a timeout of 30 seconds will be used.
42 changes: 35 additions & 7 deletions shared/cliconfig/remote.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cliconfig

import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
Expand All @@ -12,6 +14,7 @@ import (
"strings"

"github.com/zitadel/oidc/v2/pkg/oidc"
"golang.org/x/crypto/ssh"

"github.com/lxc/incus/client"
"github.com/lxc/incus/shared/api"
Expand Down Expand Up @@ -304,22 +307,47 @@ func (c *Config) getConnectionArgs(name string) (*incus.ConnectionArgs, error) {
// Golang has deprecated all methods relating to PEM encryption due to a vulnerability.
// However, the weakness does not make PEM unsafe for our purposes as it pertains to password protection on the
// key file (client.key is only readable to the user in any case), so we'll ignore deprecation.
if x509.IsEncryptedPEMBlock(pemKey) { //nolint:staticcheck
isEncrypted := x509.IsEncryptedPEMBlock(pemKey) //nolint:staticcheck
isSSH := pemKey.Type == "OPENSSH PRIVATE KEY"
if isEncrypted || isSSH {
if c.PromptPassword == nil {
return nil, fmt.Errorf("Private key is password protected and no helper was configured")
}

password, err := c.PromptPassword("client.crt")
password, err := c.PromptPassword("client.key")
if err != nil {
return nil, err
}

derKey, err := x509.DecryptPEMBlock(pemKey, []byte(password)) //nolint:staticcheck
if err != nil {
return nil, err
}
if isSSH {
sshKey, err := ssh.ParseRawPrivateKeyWithPassphrase(content, []byte(password))
if err != nil {
return nil, err
}

content = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: derKey})
ecdsaKey, _ := (sshKey).(*ecdsa.PrivateKey)

Check failure on line 328 in shared/cliconfig/remote.go

View workflow job for this annotation

GitHub Actions / Code

unchecked-type-assertion: type cast result is unchecked in (sshKey).(*ecdsa.PrivateKey) - type assertion result ignored (revive)
rsaKey, _ := (sshKey).(*rsa.PrivateKey)

Check failure on line 329 in shared/cliconfig/remote.go

View workflow job for this annotation

GitHub Actions / Code

unchecked-type-assertion: type cast result is unchecked in (sshKey).(*rsa.PrivateKey) - type assertion result ignored (revive)
if ecdsaKey != nil {
derKey, err := x509.MarshalECPrivateKey(ecdsaKey)
if err != nil {
return nil, err
}

content = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
} else if rsaKey != nil {
derKey := x509.MarshalPKCS1PrivateKey(rsaKey)
content = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
} else {
return nil, fmt.Errorf("Unsupported key type: %T", sshKey)
}
} else {
derKey, err := x509.DecryptPEMBlock(pemKey, []byte(password)) //nolint:staticcheck
if err != nil {
return nil, err
}

content = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
}
}

args.TLSClientKey = string(content)
Expand Down
1 change: 1 addition & 0 deletions test/godeps.list
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ github.com/pkg/sftp
github.com/zitadel/oidc/v2/pkg/client/rp
github.com/zitadel/oidc/v2/pkg/http
github.com/zitadel/oidc/v2/pkg/oidc
golang.org/x/crypto/ssh
golang.org/x/oauth2
gopkg.in/yaml.v2

0 comments on commit 4577279

Please sign in to comment.