mirror of
https://github.com/go-task/task.git
synced 2025-12-16 19:57:43 +01:00
remove passphrase
This commit is contained in:
@@ -105,10 +105,9 @@ complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled GENTLE_FORCE" -l
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l offline -d 'use only local or cached Taskfiles'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l timeout -d 'timeout for remote Taskfile downloads'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l expiry -d 'cache expiry duration'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cacert -d 'custom CA certificate for TLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert -d 'client certificate for mTLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert-key -d 'client certificate private key' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert-key-pass -d 'passphrase for private key'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cacert -d 'custom CA certificate for TLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert -d 'client certificate for mTLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert-key -d 'client certificate private key' -r
|
||||
|
||||
# RemoteTaskfiles experiment - Operations
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l download -d 'download remote Taskfile'
|
||||
|
||||
@@ -75,7 +75,6 @@ Register-ArgumentCompleter -CommandName task -ScriptBlock {
|
||||
$completions += [CompletionResult]::new('--cacert', '--cacert', [CompletionResultType]::ParameterName, 'custom CA certificate')
|
||||
$completions += [CompletionResult]::new('--cert', '--cert', [CompletionResultType]::ParameterName, 'client certificate')
|
||||
$completions += [CompletionResult]::new('--cert-key', '--cert-key', [CompletionResultType]::ParameterName, 'client private key')
|
||||
$completions += [CompletionResult]::new('--cert-key-pass', '--cert-key-pass', [CompletionResultType]::ParameterName, 'private key passphrase')
|
||||
# Operations
|
||||
$completions += [CompletionResult]::new('--download', '--download', [CompletionResultType]::ParameterName, 'download remote Taskfile')
|
||||
$completions += [CompletionResult]::new('--clear-cache', '--clear-cache', [CompletionResultType]::ParameterName, 'clear cache')
|
||||
|
||||
@@ -89,7 +89,6 @@ _task() {
|
||||
'(--cacert)--cacert[custom CA certificate for TLS]:file:_files'
|
||||
'(--cert)--cert[client certificate for mTLS]:file:_files'
|
||||
'(--cert-key)--cert-key[client certificate private key]:file:_files'
|
||||
'(--cert-key-pass)--cert-key-pass[passphrase for private key]: '
|
||||
)
|
||||
fi
|
||||
|
||||
|
||||
14
executor.go
14
executor.go
@@ -39,7 +39,6 @@ type (
|
||||
CACert string
|
||||
Cert string
|
||||
CertKey string
|
||||
CertKeyPass string
|
||||
Watch bool
|
||||
Verbose bool
|
||||
Silent bool
|
||||
@@ -296,19 +295,6 @@ func (o *certKeyOption) ApplyToExecutor(e *Executor) {
|
||||
e.CertKey = o.certKey
|
||||
}
|
||||
|
||||
// WithCertKeyPass sets the passphrase for the client certificate key.
|
||||
func WithCertKeyPass(certKeyPass string) ExecutorOption {
|
||||
return &certKeyPassOption{certKeyPass: certKeyPass}
|
||||
}
|
||||
|
||||
type certKeyPassOption struct {
|
||||
certKeyPass string
|
||||
}
|
||||
|
||||
func (o *certKeyPassOption) ApplyToExecutor(e *Executor) {
|
||||
e.CertKeyPass = o.certKeyPass
|
||||
}
|
||||
|
||||
// WithWatch tells the [Executor] to keep running in the background and watch
|
||||
// for changes to the fingerprint of the tasks that are run. When changes are
|
||||
// detected, a new task run is triggered.
|
||||
|
||||
@@ -79,7 +79,6 @@ var (
|
||||
CACert string
|
||||
Cert string
|
||||
CertKey string
|
||||
CertKeyPass string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -162,7 +161,6 @@ func init() {
|
||||
pflag.StringVar(&CACert, "cacert", getConfig(config, func() *string { return config.Remote.CACert }, ""), "Path to a custom CA certificate for HTTPS connections.")
|
||||
pflag.StringVar(&Cert, "cert", getConfig(config, func() *string { return config.Remote.Cert }, ""), "Path to a client certificate for HTTPS connections.")
|
||||
pflag.StringVar(&CertKey, "cert-key", getConfig(config, func() *string { return config.Remote.CertKey }, ""), "Path to a client certificate key for HTTPS connections.")
|
||||
pflag.StringVar(&CertKeyPass, "cert-key-pass", getConfig(config, func() *string { return config.Remote.CertKeyPass }, ""), "Passphrase for the client certificate key.")
|
||||
}
|
||||
pflag.Parse()
|
||||
}
|
||||
@@ -213,10 +211,6 @@ func Validate() error {
|
||||
return errors.New("task: --cert and --cert-key must be provided together")
|
||||
}
|
||||
|
||||
if CertKeyPass != "" && Cert == "" {
|
||||
return errors.New("task: --cert-key-pass requires --cert and --cert-key")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -260,7 +254,6 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
||||
task.WithCACert(CACert),
|
||||
task.WithCert(Cert),
|
||||
task.WithCertKey(CertKey),
|
||||
task.WithCertKeyPass(CertKeyPass),
|
||||
task.WithWatch(Watch),
|
||||
task.WithVerbose(Verbose),
|
||||
task.WithSilent(Silent),
|
||||
|
||||
2
setup.go
2
setup.go
@@ -60,7 +60,6 @@ func (e *Executor) getRootNode() (taskfile.Node, error) {
|
||||
taskfile.WithCACert(e.CACert),
|
||||
taskfile.WithCert(e.Cert),
|
||||
taskfile.WithCertKey(e.CertKey),
|
||||
taskfile.WithCertKeyPass(e.CertKeyPass),
|
||||
)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errors.TaskfileNotFoundError{
|
||||
@@ -94,7 +93,6 @@ func (e *Executor) readTaskfile(node taskfile.Node) error {
|
||||
taskfile.WithReaderCACert(e.CACert),
|
||||
taskfile.WithReaderCert(e.Cert),
|
||||
taskfile.WithReaderCertKey(e.CertKey),
|
||||
taskfile.WithReaderCertKeyPass(e.CertKeyPass),
|
||||
taskfile.WithDebugFunc(debugFunc),
|
||||
taskfile.WithPromptFunc(promptFunc),
|
||||
)
|
||||
|
||||
@@ -7,13 +7,12 @@ type (
|
||||
// designed to be embedded in other node types so that this boilerplate code
|
||||
// does not need to be repeated.
|
||||
baseNode struct {
|
||||
parent Node
|
||||
dir string
|
||||
checksum string
|
||||
caCert string
|
||||
cert string
|
||||
certKey string
|
||||
certKeyPass string
|
||||
parent Node
|
||||
dir string
|
||||
checksum string
|
||||
caCert string
|
||||
cert string
|
||||
certKey string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -76,9 +75,3 @@ func WithCertKey(certKey string) NodeOption {
|
||||
node.certKey = certKey
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertKeyPass(certKeyPass string) NodeOption {
|
||||
return func(node *baseNode) {
|
||||
node.certKeyPass = certKeyPass
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type HTTPNode struct {
|
||||
|
||||
// buildHTTPClient creates an HTTP client with optional TLS configuration.
|
||||
// If no certificate options are provided, it returns http.DefaultClient.
|
||||
func buildHTTPClient(insecure bool, caCert, cert, certKey, certKeyPass string) (*http.Client, error) {
|
||||
func buildHTTPClient(insecure bool, caCert, cert, certKey string) (*http.Client, error) {
|
||||
// Validate that cert and certKey are provided together
|
||||
if (cert != "" && certKey == "") || (cert == "" && certKey != "") {
|
||||
return nil, fmt.Errorf("both --cert and --cert-key must be provided together")
|
||||
@@ -56,15 +56,7 @@ func buildHTTPClient(insecure bool, caCert, cert, certKey, certKeyPass string) (
|
||||
|
||||
// Load client certificate and key if provided
|
||||
if cert != "" && certKey != "" {
|
||||
var clientCert tls.Certificate
|
||||
var err error
|
||||
|
||||
if certKeyPass != "" {
|
||||
// Load encrypted private key
|
||||
clientCert, err = loadCertWithEncryptedKey(cert, certKey, certKeyPass)
|
||||
} else {
|
||||
clientCert, err = tls.LoadX509KeyPair(cert, certKey)
|
||||
}
|
||||
clientCert, err := tls.LoadX509KeyPair(cert, certKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load client certificate: %w", err)
|
||||
}
|
||||
@@ -78,46 +70,6 @@ func buildHTTPClient(insecure bool, caCert, cert, certKey, certKeyPass string) (
|
||||
}, nil
|
||||
}
|
||||
|
||||
// loadCertWithEncryptedKey loads a certificate with an encrypted private key.
|
||||
func loadCertWithEncryptedKey(certFile, keyFile, passphrase string) (tls.Certificate, error) {
|
||||
certPEM, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to read certificate file: %w", err)
|
||||
}
|
||||
|
||||
keyPEM, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to read key file: %w", err)
|
||||
}
|
||||
|
||||
// Try to decrypt the private key
|
||||
decryptedKey, err := decryptPEMKey(keyPEM, passphrase)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to decrypt private key: %w", err)
|
||||
}
|
||||
|
||||
return tls.X509KeyPair(certPEM, decryptedKey)
|
||||
}
|
||||
|
||||
// decryptPEMKey attempts to decrypt a PEM-encoded private key.
|
||||
func decryptPEMKey(keyPEM []byte, passphrase string) ([]byte, error) {
|
||||
// For PKCS#8 encrypted keys, we need to parse and decrypt them
|
||||
// The standard library doesn't directly support encrypted PKCS#8,
|
||||
// so we try to parse it as-is first (in case it's not actually encrypted)
|
||||
// For now, we support unencrypted keys and return an error for encrypted ones
|
||||
// that require external libraries to decrypt.
|
||||
|
||||
// Try to parse as unencrypted first
|
||||
_, err := tls.X509KeyPair([]byte("-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----"), keyPEM)
|
||||
if err == nil {
|
||||
return keyPEM, nil
|
||||
}
|
||||
|
||||
// TODO: Add support for encrypted PKCS#8 keys using x/crypto/pkcs8
|
||||
// This would require adding a dependency on golang.org/x/crypto
|
||||
return nil, fmt.Errorf("encrypted private keys require the key to be decrypted externally, or use an unencrypted key")
|
||||
}
|
||||
|
||||
func NewHTTPNode(
|
||||
entrypoint string,
|
||||
dir string,
|
||||
@@ -134,7 +86,7 @@ func NewHTTPNode(
|
||||
}
|
||||
|
||||
// Build HTTP client with TLS configuration from node options
|
||||
client, err := buildHTTPClient(insecure, base.caCert, base.cert, base.certKey, base.certKeyPass)
|
||||
client, err := buildHTTPClient(insecure, base.caCert, base.cert, base.certKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestBuildHTTPClient_Default(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// When no TLS customization is needed, should return http.DefaultClient
|
||||
client, err := buildHTTPClient(false, "", "", "", "")
|
||||
client, err := buildHTTPClient(false, "", "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.DefaultClient, client)
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func TestBuildHTTPClient_Default(t *testing.T) {
|
||||
func TestBuildHTTPClient_Insecure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := buildHTTPClient(true, "", "", "", "")
|
||||
client, err := buildHTTPClient(true, "", "", "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
assert.NotEqual(t, http.DefaultClient, client)
|
||||
@@ -92,10 +92,10 @@ func TestBuildHTTPClient_CACert(t *testing.T) {
|
||||
|
||||
// Generate a valid CA certificate
|
||||
caCertPEM := generateTestCACert(t)
|
||||
err := os.WriteFile(caCertPath, caCertPEM, 0600)
|
||||
err := os.WriteFile(caCertPath, caCertPEM, 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := buildHTTPClient(false, caCertPath, "", "", "")
|
||||
client, err := buildHTTPClient(false, caCertPath, "", "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
assert.NotEqual(t, http.DefaultClient, client)
|
||||
@@ -110,7 +110,7 @@ func TestBuildHTTPClient_CACert(t *testing.T) {
|
||||
func TestBuildHTTPClient_CACertNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := buildHTTPClient(false, "/nonexistent/ca.crt", "", "", "")
|
||||
client, err := buildHTTPClient(false, "/nonexistent/ca.crt", "", "")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "failed to read CA certificate")
|
||||
@@ -122,10 +122,10 @@ func TestBuildHTTPClient_CACertInvalid(t *testing.T) {
|
||||
// Create a temporary file with invalid content
|
||||
tempDir := t.TempDir()
|
||||
caCertPath := filepath.Join(tempDir, "invalid.crt")
|
||||
err := os.WriteFile(caCertPath, []byte("not a valid certificate"), 0600)
|
||||
err := os.WriteFile(caCertPath, []byte("not a valid certificate"), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := buildHTTPClient(false, caCertPath, "", "", "")
|
||||
client, err := buildHTTPClient(false, caCertPath, "", "")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "failed to parse CA certificate")
|
||||
@@ -134,7 +134,7 @@ func TestBuildHTTPClient_CACertInvalid(t *testing.T) {
|
||||
func TestBuildHTTPClient_CertWithoutKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := buildHTTPClient(false, "", "/path/to/cert.crt", "", "")
|
||||
client, err := buildHTTPClient(false, "", "/path/to/cert.crt", "")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "both --cert and --cert-key must be provided together")
|
||||
@@ -143,7 +143,7 @@ func TestBuildHTTPClient_CertWithoutKey(t *testing.T) {
|
||||
func TestBuildHTTPClient_KeyWithoutCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := buildHTTPClient(false, "", "", "/path/to/key.pem", "")
|
||||
client, err := buildHTTPClient(false, "", "", "/path/to/key.pem")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "both --cert and --cert-key must be provided together")
|
||||
@@ -159,12 +159,12 @@ func TestBuildHTTPClient_CertAndKey(t *testing.T) {
|
||||
|
||||
// Generate a self-signed certificate and key for testing
|
||||
cert, key := generateTestCertAndKey(t)
|
||||
err := os.WriteFile(certPath, cert, 0600)
|
||||
err := os.WriteFile(certPath, cert, 0o600)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(keyPath, key, 0600)
|
||||
err = os.WriteFile(keyPath, key, 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := buildHTTPClient(false, "", certPath, keyPath, "")
|
||||
client, err := buildHTTPClient(false, "", certPath, keyPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
assert.NotEqual(t, http.DefaultClient, client)
|
||||
@@ -179,7 +179,7 @@ func TestBuildHTTPClient_CertAndKey(t *testing.T) {
|
||||
func TestBuildHTTPClient_CertNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := buildHTTPClient(false, "", "/nonexistent/cert.crt", "/nonexistent/key.pem", "")
|
||||
client, err := buildHTTPClient(false, "", "/nonexistent/cert.crt", "/nonexistent/key.pem")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.Contains(t, err.Error(), "failed to load client certificate")
|
||||
@@ -194,11 +194,11 @@ func TestBuildHTTPClient_InsecureWithCACert(t *testing.T) {
|
||||
|
||||
// Generate a valid CA certificate
|
||||
caCertPEM := generateTestCACert(t)
|
||||
err := os.WriteFile(caCertPath, caCertPEM, 0600)
|
||||
err := os.WriteFile(caCertPath, caCertPEM, 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Both insecure and CA cert can be set together
|
||||
client, err := buildHTTPClient(true, caCertPath, "", "", "")
|
||||
client, err := buildHTTPClient(true, caCertPath, "", "")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ type (
|
||||
caCert string
|
||||
cert string
|
||||
certKey string
|
||||
certKeyPass string
|
||||
debugFunc DebugFunc
|
||||
promptFunc PromptFunc
|
||||
promptMutex sync.Mutex
|
||||
@@ -225,19 +224,6 @@ func (o *readerCertKeyOption) ApplyToReader(r *Reader) {
|
||||
r.certKey = o.certKey
|
||||
}
|
||||
|
||||
// WithReaderCertKeyPass sets the passphrase for the client certificate key.
|
||||
func WithReaderCertKeyPass(certKeyPass string) ReaderOption {
|
||||
return &readerCertKeyPassOption{certKeyPass: certKeyPass}
|
||||
}
|
||||
|
||||
type readerCertKeyPassOption struct {
|
||||
certKeyPass string
|
||||
}
|
||||
|
||||
func (o *readerCertKeyPassOption) ApplyToReader(r *Reader) {
|
||||
r.certKeyPass = o.certKeyPass
|
||||
}
|
||||
|
||||
// Read will read the Taskfile defined by the [Reader]'s [Node] and recurse
|
||||
// through any [ast.Includes] it finds, reading each included Taskfile and
|
||||
// building an [ast.TaskfileGraph] as it goes. If any errors occur, they will be
|
||||
@@ -328,7 +314,6 @@ func (r *Reader) include(ctx context.Context, node Node) error {
|
||||
WithCACert(r.caCert),
|
||||
WithCert(r.cert),
|
||||
WithCertKey(r.certKey),
|
||||
WithCertKeyPass(r.certKeyPass),
|
||||
)
|
||||
if err != nil {
|
||||
if include.Optional {
|
||||
|
||||
@@ -24,7 +24,6 @@ type Remote struct {
|
||||
CACert *string `yaml:"cacert"`
|
||||
Cert *string `yaml:"cert"`
|
||||
CertKey *string `yaml:"cert-key"`
|
||||
CertKeyPass *string `yaml:"cert-key-pass"`
|
||||
}
|
||||
|
||||
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||
@@ -49,7 +48,6 @@ func (t *TaskRC) Merge(other *TaskRC) {
|
||||
t.Remote.CACert = cmp.Or(other.Remote.CACert, t.Remote.CACert)
|
||||
t.Remote.Cert = cmp.Or(other.Remote.Cert, t.Remote.Cert)
|
||||
t.Remote.CertKey = cmp.Or(other.Remote.CertKey, t.Remote.CertKey)
|
||||
t.Remote.CertKeyPass = cmp.Or(other.Remote.CertKeyPass, t.Remote.CertKeyPass)
|
||||
|
||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||
|
||||
@@ -279,16 +279,17 @@ task --taskfile https://secure.example.com/Taskfile.yml \
|
||||
--cert-key /path/to/client.key
|
||||
```
|
||||
|
||||
If your private key is encrypted with a passphrase, you can provide it using
|
||||
the `--cert-key-pass` flag:
|
||||
::: warning
|
||||
|
||||
Encrypted private keys are not currently supported. If your key is encrypted,
|
||||
you must decrypt it first:
|
||||
|
||||
```shell
|
||||
task --taskfile https://secure.example.com/Taskfile.yml \
|
||||
--cert /path/to/client.crt \
|
||||
--cert-key /path/to/client.key \
|
||||
--cert-key-pass "your-passphrase"
|
||||
openssl rsa -in encrypted.key -out decrypted.key
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
These options can also be configured in the [configuration file](#configuration).
|
||||
|
||||
## Caching & Running Offline
|
||||
@@ -339,7 +340,6 @@ remote:
|
||||
cacert: ""
|
||||
cert: ""
|
||||
cert-key: ""
|
||||
cert-key-pass: ""
|
||||
```
|
||||
|
||||
#### `insecure`
|
||||
@@ -421,15 +421,3 @@ remote:
|
||||
remote:
|
||||
cert-key: "/path/to/client.key"
|
||||
```
|
||||
|
||||
#### `cert-key-pass`
|
||||
|
||||
- **Type**: `string`
|
||||
- **Default**: `""`
|
||||
- **Description**: Passphrase for the client certificate private key (if
|
||||
encrypted)
|
||||
|
||||
```yaml
|
||||
remote:
|
||||
cert-key-pass: "your-passphrase"
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user