2018-08-27 23:23:48 +01:00
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
2018-12-18 11:55:55 +00:00
"crypto/x509/pkix"
"encoding/json"
"fmt"
2019-01-18 10:36:58 +00:00
"path/filepath"
2018-12-18 11:55:55 +00:00
2018-08-27 23:23:48 +01:00
"github.com/spf13/cobra"
2019-01-18 10:36:58 +00:00
"github.com/zeebo/errs"
2018-08-27 23:23:48 +01:00
2018-12-18 11:55:55 +00:00
"storj.io/storj/internal/fpath"
2019-01-18 10:36:58 +00:00
"storj.io/storj/pkg/certificates"
"storj.io/storj/pkg/cfgstruct"
"storj.io/storj/pkg/identity"
2018-12-18 11:55:55 +00:00
"storj.io/storj/pkg/peertls"
2018-08-27 23:23:48 +01:00
"storj.io/storj/pkg/process"
)
var (
rootCmd = & cobra . Command {
Use : "identity" ,
Short : "Identity management" ,
}
2019-01-18 10:36:58 +00:00
newServiceCmd = & cobra . Command {
Use : "new <service>" ,
Short : "Create a new full identity for a service" ,
Args : cobra . ExactArgs ( 1 ) ,
RunE : cmdNewService ,
Annotations : map [ string ] string { "type" : "setup" } ,
}
csrCmd = & cobra . Command {
Use : "csr <service>" ,
Short : "Send a certificate signing request for a service's CA certificate" ,
Args : cobra . ExactArgs ( 1 ) ,
RunE : cmdCSR ,
Annotations : map [ string ] string { "type" : "setup" } ,
}
//nolint
config struct {
Difficulty uint64 ` default:"15" help:"minimum difficulty for identity generation" `
Concurrency uint ` default:"4" help:"number of concurrent workers for certificate authority generation" `
ParentCertPath string ` help:"path to the parent authority's certificate chain" `
ParentKeyPath string ` help:"path to the parent authority's private key" `
Signer certificates . CertClientConfig
}
2019-01-22 12:35:48 +00:00
identityDir string
defaultIdentityDir = fpath . ApplicationDir ( "storj" , "identity" )
2018-08-27 23:23:48 +01:00
)
2019-01-18 10:36:58 +00:00
func init ( ) {
2019-01-22 12:35:48 +00:00
rootCmd . PersistentFlags ( ) . StringVar ( & identityDir , "identity-dir" , defaultIdentityDir , "root directory for identity output" )
2019-01-18 10:36:58 +00:00
rootCmd . AddCommand ( newServiceCmd )
rootCmd . AddCommand ( csrCmd )
2019-01-22 12:35:48 +00:00
cfgstruct . Bind ( newServiceCmd . Flags ( ) , & config , cfgstruct . CredsDir ( defaultIdentityDir ) )
cfgstruct . Bind ( csrCmd . Flags ( ) , & config , cfgstruct . CredsDir ( defaultIdentityDir ) )
2019-01-18 10:36:58 +00:00
}
2018-08-27 23:23:48 +01:00
func main ( ) {
process . Exec ( rootCmd )
}
2018-12-18 11:55:55 +00:00
2019-01-18 10:36:58 +00:00
func serviceDirectory ( serviceName string ) string {
2019-01-22 12:35:48 +00:00
return filepath . Join ( identityDir , serviceName )
2019-01-18 10:36:58 +00:00
}
func cmdNewService ( cmd * cobra . Command , args [ ] string ) error {
serviceDir := serviceDirectory ( args [ 0 ] )
caCertPath := filepath . Join ( serviceDir , "ca.cert" )
caKeyPath := filepath . Join ( serviceDir , "ca.key" )
identCertPath := filepath . Join ( serviceDir , "identity.cert" )
identKeyPath := filepath . Join ( serviceDir , "identity.key" )
caConfig := identity . CASetupConfig {
CertPath : caCertPath ,
KeyPath : caKeyPath ,
Difficulty : config . Difficulty ,
Concurrency : config . Concurrency ,
ParentCertPath : config . ParentCertPath ,
ParentKeyPath : config . ParentKeyPath ,
}
if caConfig . Status ( ) != identity . NoCertNoKey {
return errs . New ( "CA certificate and/or key already exits, NOT overwriting!" )
}
ca , caerr := caConfig . Create ( process . Ctx ( cmd ) )
identConfig := identity . SetupConfig {
CertPath : identCertPath ,
KeyPath : identKeyPath ,
}
if identConfig . Status ( ) != identity . NoCertNoKey {
return errs . New ( "Identity certificate and/or key already exits, NOT overwriting!" )
}
_ , iderr := identConfig . Create ( ca )
return errs . Combine ( caerr , iderr )
}
func cmdCSR ( cmd * cobra . Command , args [ ] string ) error {
ctx := process . Ctx ( cmd )
serviceDir := serviceDirectory ( args [ 0 ] )
caCertPath := filepath . Join ( serviceDir , "ca.cert" )
caKeyPath := filepath . Join ( serviceDir , "ca.key" )
caConfig := identity . FullCAConfig {
CertPath : caCertPath ,
KeyPath : caKeyPath ,
}
identCertPath := filepath . Join ( serviceDir , "identity.cert" )
identKeyPath := filepath . Join ( serviceDir , "identity.key" )
identConfig := identity . Config {
CertPath : identCertPath ,
KeyPath : identKeyPath ,
}
ca , err := caConfig . Load ( )
if err != nil {
return err
}
ident , err := identConfig . Load ( )
if err != nil {
return err
}
signedChainBytes , err := config . Signer . Sign ( ctx , ident )
if err != nil {
return errs . New ( "error occurred while signing certificate: %s\n(identity files were still generated and saved, if you try again existing files will be loaded)" , err )
}
signedChain , err := identity . ParseCertChain ( signedChainBytes )
if err != nil {
return nil
}
err = caConfig . SaveBackup ( ca )
if err != nil {
return err
}
ca . Cert = signedChain [ 0 ]
ca . RestChain = signedChain [ 1 : ]
err = identity . FullCAConfig {
CertPath : caConfig . CertPath ,
} . Save ( ca )
if err != nil {
return err
}
err = identConfig . SaveBackup ( ident )
if err != nil {
return err
}
ident . RestChain = signedChain [ 1 : ]
ident . CA = ca . Cert
err = identity . Config {
CertPath : identConfig . CertPath ,
} . Save ( ident )
if err != nil {
return err
}
return nil
}
2018-12-18 11:55:55 +00:00
func printExtensions ( cert [ ] byte , exts [ ] pkix . Extension ) error {
hash , err := peertls . SHA256Hash ( cert )
if err != nil {
return err
}
b64Hash , err := json . Marshal ( hash )
if err != nil {
return err
}
fmt . Printf ( "Cert hash: %s\n" , b64Hash )
fmt . Println ( "Extensions:" )
for _ , e := range exts {
var data interface { }
switch e . Id . String ( ) {
case peertls . ExtensionIDs [ peertls . RevocationExtID ] . String ( ) :
var rev peertls . Revocation
if err := rev . Unmarshal ( e . Value ) ; err != nil {
return err
}
data = rev
default :
data = e . Value
}
out , err := json . MarshalIndent ( data , "" , " " )
if err != nil {
return err
}
fmt . Printf ( "\t%s: %s\n" , e . Id , out )
}
return nil
}