Skip to content
Snippets Groups Projects
Commit fe53f90b authored by Kevin Lyda's avatar Kevin Lyda :speech_balloon: Committed by Niall Sheridan
Browse files

Add a public_file_prefix option to cashier.conf

Allow the client to save the public key and public cert to files
that start with public_file_prefix and end with .pub and -cert.pub
respectively.

This is the naming scheme the ssh IdentityFile config option supported
for certs starting in version 5.4p1. Starting in version 7.2p1, an
additional option, CertificateFile, was added, but the IdentityFile-only
method with those names still works.

Used in conjunction with a user's ~/.ssh/config file setting
IdentitiesOnly and IdentityFile, this change will allow for multiple
ssh CAs for different services.

Note that this will resolve #49 .
parent 450bee5d
No related branches found
No related tags found
No related merge requests found
...@@ -2,8 +2,8 @@ config.json ...@@ -2,8 +2,8 @@ config.json
cashierd.conf cashierd.conf
tmp tmp
cashier /cashier
cashierd /cashierd
signing_key signing_key
http.log http.log
...@@ -253,15 +253,30 @@ For the server you need the following: ...@@ -253,15 +253,30 @@ For the server you need the following:
Once the server is up and running you'll need to configure your client. Once the server is up and running you'll need to configure your client.
The client is configured using either a [HCL](https://github.com/hashicorp/hcl) configuration file - [example](example-client.conf) - or command-line flags. The client is configured using either a [HCL](https://github.com/hashicorp/hcl) configuration file - [example](example-client.conf) - or command-line flags.
- `--ca` CA server (default "http://localhost:10000").
- `--config` Path to config file (default "~/.cashier.conf").
- `--key_size` Key size. Ignored for ed25519 keys (default 2048).
- `--key_type` Type of private key to generate - rsa, ecdsa or ed25519 (default "rsa").
- `--public_file_prefix` Prefix for filename for public key and cert (optional, no default). The public key is put in a file with `.pub` appended to it; the public cert file in a file with `-cert.pub` appended to it.
- `--validity` Key validity (default 24h).
Running the `cashier` cli tool will open a browser window at the configured CA address. Running the `cashier` cli tool will open a browser window at the configured CA address.
The CA will redirect to the auth provider for authorisation, and redirect back to the CA where the access token will printed. The CA will redirect to the auth provider for authorisation, and redirect back to the CA where the access token will printed.
Copy the access token. In the terminal where you ran the `cashier` cli paste the token at the prompt. Copy the access token. In the terminal where you ran the `cashier` cli paste the token at the prompt.
The client will then generate a new ssh key-pair and send the public part to the server (along with the access token). The client will then generate a new ssh key-pair and send the public part to the server (along with the access token).
Once signed the client will install the key and signed certificate in your ssh agent. When the certificate expires it will be removed automatically from the agent. Once signed the client will install the key and signed certificate in your ssh agent. When the certificate expires it will be removed automatically from the agent.
If you set `public_file_prefix` then the public key and public cert will be written to the files that start with `public_file_prefix` and end with `.pub` and `-cert.pub` respectively.
In your `ssh_config` you can load these for a given host with the `IdentityFile` and `CertificateFile`. However prior to OpenSSH version 7.2p1 the latter option didn't exist.
In that case you could specify `~/.ssh/some-identity` as your `IdentityFile` and OpenSSH would look in `~/.ssh/some-identity.pub` and `~/.ssh/some-identity-cert.pub`.
Starting with 7.2p1 the two options exist in the `ssh_config` and you'll need to use the full paths to them.
Note that like these `ssh_config` options, the `public_file_prefix` supports tilde expansion.
## Configuring SSH ## Configuring SSH
The ssh client needs no special configuration, just a running ssh-agent. The ssh client needs no special configuration, just a running `ssh-agent`.
The ssh server needs to trust the public part of the CA signing key. Add something like the following to your sshd_config: The ssh server needs to trust the public part of the CA signing key. Add something like the following to your `sshd_config`:
``` ```
TrustedUserCAKeys /etc/ssh/ca.pub TrustedUserCAKeys /etc/ssh/ca.pub
``` ```
......
...@@ -3,8 +3,10 @@ package client ...@@ -3,8 +3,10 @@ package client
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
...@@ -16,6 +18,22 @@ import ( ...@@ -16,6 +18,22 @@ import (
"golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/agent"
) )
// SavePublicFiles installs the public part of the cert and key.
func SavePublicFiles(prefix string, cert *ssh.Certificate, pub ssh.PublicKey) error {
if prefix == "" {
return nil
}
pubTxt := ssh.MarshalAuthorizedKey(pub)
certPubTxt := []byte(cert.Type() + " " + base64.StdEncoding.EncodeToString(cert.Marshal()))
if err := ioutil.WriteFile(prefix+".pub", pubTxt, 0644); err != nil {
return err
}
err := ioutil.WriteFile(prefix+"-cert.pub", certPubTxt, 0644)
return err
}
// InstallCert adds the private key and signed certificate to the ssh agent. // InstallCert adds the private key and signed certificate to the ssh agent.
func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error { func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error {
t := time.Unix(int64(cert.ValidBefore), 0) t := time.Unix(int64(cert.ValidBefore), 0)
......
package client package client
import ( import (
"github.com/mitchellh/go-homedir"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
...@@ -12,6 +13,7 @@ type Config struct { ...@@ -12,6 +13,7 @@ type Config struct {
Keysize int `mapstructure:"key_size"` Keysize int `mapstructure:"key_size"`
Validity string `mapstructure:"validity"` Validity string `mapstructure:"validity"`
ValidateTLSCertificate bool `mapstructure:"validate_tls_certificate"` ValidateTLSCertificate bool `mapstructure:"validate_tls_certificate"`
PublicFilePrefix string `mapstructure:"public_file_prefix"`
} }
func setDefaults() { func setDefaults() {
...@@ -19,6 +21,7 @@ func setDefaults() { ...@@ -19,6 +21,7 @@ func setDefaults() {
viper.BindPFlag("key_type", pflag.Lookup("key_type")) viper.BindPFlag("key_type", pflag.Lookup("key_type"))
viper.BindPFlag("key_size", pflag.Lookup("key_size")) viper.BindPFlag("key_size", pflag.Lookup("key_size"))
viper.BindPFlag("validity", pflag.Lookup("validity")) viper.BindPFlag("validity", pflag.Lookup("validity"))
viper.BindPFlag("public_file_prefix", pflag.Lookup("public_file_prefix"))
viper.SetDefault("validateTLSCertificate", true) viper.SetDefault("validateTLSCertificate", true)
} }
...@@ -34,5 +37,10 @@ func ReadConfig(path string) (*Config, error) { ...@@ -34,5 +37,10 @@ func ReadConfig(path string) (*Config, error) {
if err := viper.Unmarshal(c); err != nil { if err := viper.Unmarshal(c); err != nil {
return nil, err return nil, err
} }
p, err := homedir.Expand(c.PublicFilePrefix)
if err != nil {
return nil, err
}
c.PublicFilePrefix = p
return c, nil return c, nil
} }
...@@ -22,6 +22,7 @@ var ( ...@@ -22,6 +22,7 @@ var (
keysize = pflag.Int("key_size", 2048, "Key size. Ignored for ed25519 keys") keysize = pflag.Int("key_size", 2048, "Key size. Ignored for ed25519 keys")
validity = pflag.Duration("validity", time.Hour*24, "Key validity") validity = pflag.Duration("validity", time.Hour*24, "Key validity")
keytype = pflag.String("key_type", "rsa", "Type of private key to generate - rsa, ecdsa or ed25519") keytype = pflag.String("key_type", "rsa", "Type of private key to generate - rsa, ecdsa or ed25519")
publicFilePrefix = pflag.String("public_file_prefix", "", "Prefix for filename for public key and cert (optional, no default)")
) )
func main() { func main() {
...@@ -58,5 +59,8 @@ func main() { ...@@ -58,5 +59,8 @@ func main() {
if err := client.InstallCert(a, cert, priv); err != nil { if err := client.InstallCert(a, cert, priv); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
if err := client.SavePublicFiles(c.PublicFilePrefix, cert, pub); err != nil {
log.Fatalln(err)
}
fmt.Println("Credentials added.") fmt.Println("Credentials added.")
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment