Code Monkey home page Code Monkey logo

cashier's Introduction

Cashier

Cashier is a SSH Certificate Authority (CA).

OpenSSH supports authentication using SSH certificates. Certificates contain a public key, identity information and are signed with a standard SSH key.

Unlike ssh keys, certificates can contain additional information:

  • Which user(s) may use the certificate
  • When the certificate is valid from
  • When the certificate expires
  • Permissions

Other benefits of certificates:

  • Unlike keys certificates don't need to be distributed to every machine - the sshd just needs to trust the key that signed the certificate.
  • This also works for host keys - machines can get new (signed) host certs which clients can authenticate. No more blindly typing "yes".
  • Certificates can be revoked.

See also the CERTIFICATES section of ssh-keygen(1)

How it works

The user wishes to ssh to a production machine.

They run a command which opens the CA site (e.g. https://sshca.exampleorg.com) in a browser and they login.

The CA displays a token which the user copies.

The user provides the token to the client. The client generates a new ssh key-pair.

The client sends the ssh public key to the CA along with the token.

The CA verifies the token and signs the public key with the signing key and returns the signed certificate.

The client receives the certificate and loads it and the private key into the ssh agent.

The user can now ssh to the production machine, and continue to ssh to any machine that trusts the CA signing key until the certificate is revoked or expires or is removed from the agent.

Installing

Stable versions can be obtained from the release page. Releases contain both static and dynamically linked executables. Statically linked executables do not have sqlite support.

Note that installing using standard Go tools is possible, but the master branch should be considered unstable.

The server requires a configuration file (sample config).

See the configuration section for more detail.

Docker

A docker image is available. Example usage:

docker run -it --rm -p 10000:10000 --name cashier -v ${PWD}:/cashier nsheridan/cashier

Requirements

Server

Go 1.10 or 1.11, though it may work with earlier versions.

Client

  • Go 1.10 or 1.11 or later, though it may work with earlier versions.
  • OpenSSH 5.6 or newer.
  • A working SSH agent (note that the GPG agent does not handle certificates)

Note: Cashier has only been tested on macOS and Linux.

Configuration

Configuration is divided into different sections: server, auth, ssh, and aws.

A note on files:

For any option that takes a file path as a parameter (e.g. SSH signing key, TLS key, TLS cert), the path can be one of:

  • A relative or absolute filesystem path e.g. /data/ssh_signing_key, tls/server.key.
  • An AWS S3 bucket + object path starting with /s3/ e.g. /s3/my-bucket/ssh_signing_key. You should add an aws config as needed.
  • A Google GCS bucket + object path starting with /gcs/ e.g. /gcs/my-bucket/ssh_signing_key.
  • A Vault path + key starting with /vault/ e.g. /vault/secret/cashier/ssh_signing_key. You should add a vault config as needed.

Exception to this: the http_logfile option ONLY writes to local files.

server

  • use_tls : boolean. If this is set then either tls_key and tls_cert are required, or letsencrypt_servername is required.
  • tls_key : string. Path to the TLS key. See the note on files above.
  • tls_cert : string. Path to the TLS cert. See the note on files above.
  • letsencrypt_servername: string. If set will request a certificate from LetsEncrypt. This should match the expected FQDN of the server.
  • letsencrypt_cachedir: string. Directory to cache the LetsEncrypt certificate. See the note on files above.
  • address : string. IP address to listen on. If unset the server listens on all addresses.
  • port : int. Port to listen on.
  • user : string. User to which the server drops privileges to. Note Dropping privileges might not work as expected as some threads may retain their privileges due to the limitations of the Go runtime.
  • cookie_secret: string. Authentication key for the session cookie. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/cookie_secret.
  • csrf_secret: string. Authentication key for CSRF protection. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/csrf_secret.
  • http_logfile: string. Path to the HTTP request log. Logs are written in the Common Log Format. The only valid destination for logs is a local file path.
  • require_reason: bool. Require the client to provide a reason when requesting a certificate. Defaults to false.
  • database: See below.

database

The database is used to record issued certificates for audit and revocation purposes.

  • type : string. One of mysql, sqlite or mem.
  • address : string. (mysql only) Hostname and optional port of the database server.
  • username : string. Database username.
  • password : string. Database password. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/mysql_password.
  • filename : string. (sqlite only). Path to sqlite database.
  • dbname: string (mysql only). Name of database to use.

Examples:

server {
  database {
    type = "mysql"
    address = "my-db-host.corp"
    username = "user"
    password = "passwd"
    dbname = "cashier_production"
  }

  database {
    type = "mem"
  }

  database {
    type = "sqlite"
    filename = "/data/cashier.db"
  }
}

Cashierd will not create the database for you - you need to create this. On startup cashierd will execute any schema changes. Obviously you should setup a role user for running in prodution.

auth

  • provider : string. Name of the oauth provider. Valid providers are currently "google", "github" and "gitlab".
  • oauth_client_id : string. Oauth Client ID. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/oauth_client_id.
  • oauth_client_secret : string. Oauth secret. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/oauth_client_secret.
  • oauth_callback_url : string. URL that the Oauth provider will redirect to after user authorisation. The path is hardcoded to "/auth/callback" in the source.
  • provider_opts : object. Additional options for the provider.
  • users_whitelist : array of strings. Optional list of whitelisted usernames. If missing, all users of your current domain/organization are allowed to authenticate against cashierd. For Google auth a user is an email address. For GitHub auth a user is a GitHub username.

Provider-specific options

Oauth providers can support provider-specific options - e.g. to ensure organization membership. Options are set in the provider_opts hash.

Example:

auth {
  provider = "google"
  provider_opts {
    domain = "example.com"
  }
}

Supported options:

Provider Option Notes
Github organization If this is unset then you must whitelist individual users using users_whitelist. The oauth client and secrets should be issued by the specified organization.
Gitlab allusers Allow all valid users to get signed keys. Only allowed if siteurl set.
Gitlab groups Comma separated list of valid groups. If allusers and this are unset then you must whitelist individual users using users_whitelist. Otherwise the user must be a member of one of these groups.
Gitlab siteurl Optional. The url of the Gitlab site. Default: https://gitlab.com/
Google domain If this is unset then you must whitelist individual email addresses using users_whitelist.
Microsoft groups Comma separated list of valid groups.
Microsoft tenant The domain name of the Office 365 account.

ssh

  • signing_key: string. Path to the certificate signing ssh private key. Use ssh-keygen to create the key and store it somewhere safe. See also the note on files above.
  • additional_principals: array of string. By default certificates will have one principal set - the username portion of the requester's email address. If additional_principals is set, these will be added to the certificate e.g. if your production machines use shared user accounts.
  • max_age: string. If set the server will not issue certificates with an expiration value longer than this, regardless of what the client requests. Must be a valid Go time.Duration string.
  • permissions: array of string. Specify the actions the certificate can perform. See the -O option to ssh-keygen(1) for a complete list. e.g. permissions = ["permit-pty", "permit-port-forwarding", force-command=/bin/ls", "source-address=192.168.0.0/24"]

aws

AWS configuration is only needed for accessing signing keys stored on S3, and isn't totally necessary even then.
The S3 client can be configured using any of the usual AWS-SDK means - environment variables, IAM roles etc.
It's strongly recommended that signing keys stored on S3 be locked down to specific IAM roles and encrypted using KMS.

  • region: string. AWS region the bucket resides in, e.g. us-east-1.
  • access_key: string. AWS Access Key ID. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/aws_access_key.
  • secret_key: string. AWS Secret Key. This can be a secret stored in a vault using the form /vault/path/key e.g. /vault/secret/cashier/aws_secret_key.

vault

Vault support is currently a work-in-progress.

  • address: string. URL to the vault server.
  • token: string. Auth token for the vault.

Usage

Cashier comes in two parts, a cli and a server.
The server is configured using a HCL configuration file - example.

For the server you need the following:

  • A new ssh private key. Generate one using ssh-keygen - e.g. ssh-keygen -f ssh_ca - this is your CA signing key. At this time Cashier supports RSA, ECDSA and Ed25519 keys. Important This key should be kept safe - ANY ssh key signed with this key will be able to access your machines.
  • OAuth (Google or GitHub) credentials. You may also need to set the callback URL when creating these.

Using cashier client

Once the server is up and running you'll need to configure your client.
The client is configured using either a HCL configuration file - example - 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").
  • --key_file_prefix Prefix for filename for SSH keys and cert (optional, no default). The public key is put in a file with id_<id>.pub appended to it; the public cert file in a file with id_<id>-cert.pub appended to it. The private key is stored in a file with id_<id> appended to it. is taken from the id stored on the server.
  • --validity Key validity (default 24h).

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.
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).
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 key_file_prefix then the public key and public cert will be written to the files that start with key_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 key_file_prefix supports tilde expansion.

Configuring SSH

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:

TrustedUserCAKeys /etc/ssh/ca.pub

where /etc/ssh/ca.pub contains the public part of your signing key.

If you wish to use certificate revocation you need to set the RevokedKeys option in sshd_config - see the next section.

Revoking certificates

When a certificate is signed a record is kept in the configured database. You can view issued certs at http(s)://<ca url>/admin/certs and also revoke them.
The revocation list is served at http(s)://<ca url>/revoked. To use it your sshd_config must have RevokedKeys set:

RevokedKeys /etc/ssh/revoked_keys

See the RevokedKeys option in the sshd_config man page for more.
Keeping the revoked list up to date can be done with a cron job like:

*/10 * * * * * curl -s -o /etc/ssh/revoked_keys https://sshca.example.com/revoked

Remember that the revoked_keys file must exist and must be readable by the sshd or else all ssh authentication will fail.

Future Work

  • Host certificates - only user certificates are supported at present.

Contributing

Pull requests are welcome but forking Go repos can be a pain. This is a good guide to forking and creating pull requests for Go projects.
Dependencies are vendored with govendor.

cashier's People

Contributors

allentv avatar bobjflong avatar dependabot[bot] avatar fuero avatar junaid-ali avatar lyda avatar niall-intercom avatar nsheridan avatar olive42 avatar patrickod avatar sid77 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cashier's Issues

Use better database schema management tooling

Changing the database schema is fraught with peril. An ORM isn't necessarily warranted here, but being able to incrementally define and execute schema changes would be extremely useful.

Handle encrypted signing keys

Right now trying to use a private key with a passphrase will produce errors like:

ecdsa key:

2016/05/29 18:22:53 unable to parse CA key: asn1: structure error: tags don't match (16 vs {class:3 tag:2629 length:91 isCompound:false}) {optional:false explicit:false application:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} pkcs1PrivateKey @4
exit status 1

rsa or ed25519 key:

2016/05/29 18:23:47 unable to parse CA key: asn1: structure error: length too large

Drop MongoDB support

Just stick with SQL (mysql, sqlite, maybe pg?) and use something to manage the schema

Still alive? Host certificates ahead?

Hello,

is this project still under development? Is there any timeline when you think there will be the possibility to use host certs?

Thanks,
Thomas

log X-Forwarded-For IP instead of remote IP where appropriate

Currently when deployed behind any sort of load balancing apparatus the logs will display the remote IP of the load balancer in the request logs. Most of these set an extra X-Forwarded-For header which we should log instead as the source of the request.

cashierd config file should be optional

The config file is currently mandatory. Some parts of cashierd can be configured via environment variables (I'm really not sure why I did this) but ideally the config should be entirely settable using cmdline flags and the config file should be optional.

Add privilege separation

Right now the only way to bind to privileged ports is to start the program as root. Ideally we should:

  1. start as root
  2. read TLS certificate & key, if needed
  3. bind to privileged port
  4. drop privileges to some other user (www or www-data or whatever)
  5. ...
  6. profit!

Certificate Signed By unknown authority

After the google auth I am getting the following error

Error on /auth/callback: Post https://accounts.google.com/o/oauth2/token: x509: certificate signed by unknown authority

I have generated signing_key using ssh-keygen -f ssh_ca.
Am I missing any step?
Thank You.

Doesn't work with gnome-keyring-demon...

On Ubuntu 16.04, the gnome-keyring-daemon gives this reply when I run cashier:

Your browser has been opened to visit https://sshca
Generating new key pair
Enter token: Created new window in existing browser session.
xxxxxxxxxxxxx
2017/01/10 12:37:38 error importing certificate: agent: client error: EOF

If I switch to ssh-agent it works fine. Trying to figure out where gnome-keyring-daemon logs things to...

Expiration time per certificate configuration

By default, cashier allows setting the same expiration time for every certificate. Shouldn't it have option to have separate expiration time for each of the certificate? It has several use-cases.

Datastore config is too complicated

Using a colon-delimited string for datastore configuration is complicated and annoying.
It should be converted to a key-value series. Examples:

server {
  datastore {
    type = "mysql"
    username = "user"
    password = "passwd"
    address = "my-db-host.corp:3306"
  }
}

server {
  datastore {
    type = "mongo"
    username = "user"
    password = "passwd"
    address = "my-mongo-host1.corp,my-mongo-host2.corp"
  }
}

server {
  datastore {
    type = "sqlite"
    filename = "/data/cashier.db"
  }
}

This has the benefit of making it easier to use vault for datastore passwords.
This will break existing configs so some form of detection and warning or compatibility should be employed.

Redirect http => https where possible ; add HSTS headers

In the following set of steps:

  1. navigate to root URL over plaintext HTTP
  2. complete oauth flow
  3. are redirected back to https:// callback URL

you'll be greeted with a 404 as the request handler is unable to read the auth_url out of the session cookie since in step 1 it was set on a cookie with the secure parameter meaning that it's dropped by the browser.

We should pre-empt this and redirect all http requests to https first, and also set HSTS headers to prevent subsequent visits from hitting this redirect.

cashier cmd should read CA from dotfile

having a default value doesn't make sense for other users, also passing it all the time is a PITA. let's have a really simple dotfile for storing these defaults. also makes provisioning in orgs much easier.

sql: unknown driver "sqlite3" (forgotten import?) with docker-image

Hi,

I run the docker-image as follows:

docker run -it --rm --net host --name cashier -v volumes/cashier:/cashier -v /etc/passwd:/etc/passwd:ro nsheridan/cashier

The cashierd.conf contains

  database {
    type = "sqlite"
    filename = "/cashier/cashier.db"
  }

I created the database file with sqlite3 cashier.db before running the container.

Stefan

Package for Debian

It'd be very nice to be able to install the client and server separately via Debian packages for automated provisioning of new cashier instances.

More logs

Potentially 2 logs - a debug/info logging actions, and a weblog logging http interactions.

Support critical options in certs

SSH certificates support both extensions and critical options. The former holds options such as permit-pty, permit-agent-forwarding, permit-X11-forwarding while critical options allows for options like source-address and force-command.
Cashier supports extensions via the permissions option. It should also support critical options.

Are there any instructions to run the server on an RHEL Instance

I cloned the cashier repo on an RHEL 7.6 EC2 Instance. I filled out the server.conf with all the necessary information. I will create a systemd script to start the cashier service automatically and upon boot. I need to know how to run the server. I run the main.go under /cashier/cmd/cashier/main.go but it tries to launch a browser for me to get a token. I believe I need to put behind a web server (NGINX, APACHE) for it to work. Any info to get the server up and running is appreciated

The lack of files causes problems.

OK, this was a longer post, but with some testing discovered there's a resolution that won't require cashier to write the private key/cert. It only needs to write the public key and the public certificate to files.

So the problem:

Say you have two ssh CAs. One signs keys for *.prod.acme-corp.com and the other signs them for *.test.acme-corp.com. With the traditional world of unsigned keys, you'd do something like this in your .ssh/config:

Host *.prod.acme-corp.com
  IdentityFile ~/.ssh/id_rsa_prod

Host *.test.acme-corp.com
  IdentityFile ~/.ssh/id_rsa_test

Normally it's a single key per device. But with signed keys, each set of servers covered by an ssh CA gets a key/cert. If we get to a world where services like Gitlab and Github are supporting signed keys the multiple key thing becomes an issue.

With the IdentitiesOnly ssh option, you can force ssh to pick a specific key with IdentityFile (and CertificateFile which we'd need for signed keys). And these options can use the public key.

The ssh-add -d command has the same issue - it also wants a filename and it too can take a public key (or public cert).

A cashier option to save the public part of the signed key/cert files allows IdentitiesOnly to work.

Allow non-hardcoded DB name for SQL driver

Currently you can specify everything about the connection except the actual MySQL database instance to use once you've connected. It'd be nice to be able to specify this alongside other parameters.

Some access tokens can be too long for the TTY

While looking at Microsoft's OAuth for Azure AD I found that the token is too long to be pasted into a terminal - TTYs typically cap out at 1024 chars while the token can be much bigger than this.

Ideally any solution here should not break existing setups.

Potential workaround: compress the access token before displaying it. Something simple like gzip + base64 encoding should work here. When the client sends a token the server should be able to accept either compressed or uncompressed tokens, to avoid version mismatch problems.

Cert revocation

There's a few steps needed here:

  • Record issued certs somewhere (i.e. a database)
  • A useful interface to mark keys as revoked
  • An API endpoint which returns a revocation list which machines can pull from

Evaluate non-organisation configuration options for github / google providers

In the case where one wants to use either the github or google authentication providers without an organisation of individuals it'd be nice to have a whitelist of either @gmail.com usernames or github usernames to which one could extend access via cashier.

Thinking it'd look like

  "provider_opts": {
    "usernames": ["patrickod", "nsheridan"]
  } 

for both, as usernames work for both @gmail.com or github. Not-so-minor edge-case for GApps folks who don't have @gmail.com addresses but to whom you'd like to extend access. Maybe for google it's an emails field instead?

Non-OAuth auth sources

I was thinking a PAM-based auth source and possibly some sort of machine-based auth would be nice.

There are certain situations where I'm in an interactive session and I'd like to get an ssh cert but lack a browser. There's obviously this way but I'm wondering if a cashierd that supports PAM auth would be an option?

It would also be nice to have a way for things like periodic jobs to be able to request an ssh key. But not really clear on how that would exactly work that would be much better than just putting ssh keys there to begin with.

Support Gitlab for OAuth

OK, I have a version that works. So now I have some questions.

First, I have an addition to the vendor branch. Would it make things easier to review if I start with a single PR to add the gitlab api to it?

Next, design! For gitlab these are the provider options I've made:

Provider Option Notes
Gitlab baseurl Optional. API url. Default: https://gitlab.com/api/v3/
Gitlab authurl Optional. Auth url. Default: https://gitlab.com/oauth/authorize
Gitlab tokenurl Optional. Token url. Default: https://gitlab.com/oauth/token
Gitlab allusers Allow all valid users to get signed keys.
Gitlab group If allusers and this are unset then you must whitelist individual users using users_whitelist. Otherwise the user must be a member of this group.

So one thing I could do is condense the *url options into a single one (baseurl) onto which api/v3/, oauth/authorize and oauth/token are appended. You can deploy it such that the url is like https://some.host/some/prefix/ but other than changing from the root to a sub dir I don't think we need to be able to split it up that much.

It won't match the gitlab api's definition of baseurl though in case that might confuse people. Maybe siteurl? It would default to https://gitlab.com/.

Next design issue is group. It might be nice if that was groups. So instead of allowing users in the "wheel" group I could allow users from the [ "wheel", "wing" ] groups. However Config.Auth.ProviderOpts is a map[string]string. Maybe we could have a ProviderLists as map[string][]string (assuming I have that definition right)? BTW the group has to be looked up on the path name of the group - anyone can use "wheel" as a name, but only one group can use "wheel" as their path.

Lastly how much foot-protection should we offer? Having gitlab.com as the auth provider should exclude allusers. If you set allusers and then set group(s) or users_whitelist you're confused, so cashierd should refuse to start till you're not confused. Or no?

Allow revoking an entire signing key

The sshd option TrustedUserCAKeys allows the specified CA file to contain multiple keys which makes rotating signing keys pretty easy. But if the signing key is compromised we should be able to revoke all certs by revoking the signing key - the sshd will automatically deny any cert signed with the revoked signing key.
This sounds easy enough to implement, but we don't currently record any data about the signing key and we probably should.

@patrickod I'm interested in your thoughts on this

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.