Skip to content

Commit

Permalink
Add support for encrypted externals
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Aug 28, 2021
1 parent c03209f commit 0346200
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
3 changes: 3 additions & 0 deletions docs/REFERENCE.md
Expand Up @@ -632,6 +632,9 @@ Entries are indexed by target name, and must have a `type` and a `url` field.
must be defined in the source state. chezmoi will not create parent directories
automatically.

The optional boolean `encrypted` field specifies whether the file or archive
is encrypted.

If `type` is `file` then the target is a file with the contents of `url`. The
optional boolean field `executable` may be set, in which case the target file
will be executable.
Expand Down
20 changes: 19 additions & 1 deletion internal/chezmoi/sourcestate.go
Expand Up @@ -42,6 +42,7 @@ const (
// An External is an external source.
type External struct {
Type ExternalType `json:"type" toml:"type" yaml:"type"`
Encrypted bool `json:"encrypted" toml:"encrypted" yaml:"encrypted"`
Exact bool `json:"exact" toml:"exact" yaml:"exact"`
Executable bool `json:"executable" toml:"executable" yaml:"executable"`
StripComponents int `json:"stripComponents" toml:"stripComponents" yaml:"stripComponents"`
Expand Down Expand Up @@ -1038,6 +1039,12 @@ func (s *SourceState) getExternalData(ctx context.Context, externalRelPath RelPa
data, err := s.system.ReadFile(cachedDataAbsPath)
switch {
case err == nil:
if external.Encrypted {
data, err = s.encryption.Decrypt(data)
if err != nil {
return nil, fmt.Errorf("%s: %s: %w", externalRelPath, external.URL, err)
}
}
return data, nil
case !errors.Is(err, fs.ErrNotExist):
return nil, err
Expand Down Expand Up @@ -1077,6 +1084,13 @@ func (s *SourceState) getExternalData(ctx context.Context, externalRelPath RelPa
return nil, err
}

if external.Encrypted {
data, err = s.encryption.Decrypt(data)
if err != nil {
return nil, fmt.Errorf("%s: %s: %w", externalRelPath, external.URL, err)
}
}

return data, nil
}

Expand Down Expand Up @@ -1468,7 +1482,11 @@ func (s *SourceState) readExternalArchive(ctx context.Context, externalRelPath R
return nil, fmt.Errorf("%s: %s: %w", externalRelPath, external.URL, err)
}
var r io.Reader = bytes.NewReader(data)
switch path := strings.ToLower(url.Path); {
path := url.Path
if external.Encrypted {
path = strings.TrimSuffix(path, s.encryption.EncryptedSuffix())
}
switch {
case strings.HasSuffix(path, ".tar.gz") || strings.HasSuffix(path, ".tgz"):
r, err = gzip.NewReader(r)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/testdata/scripts/external.txt
Expand Up @@ -18,14 +18,14 @@ cmp $HOME/.file golden/.file
chhome home3/user
chezmoi apply --force
cmp $HOME/.dir/dir/file golden/dir/file
readlink $HOME/.dir/dir/symlink file
[!windows] readlink $HOME/.dir/dir/symlink file
exists $HOME/.dir/file

# test that chezmoi reads exact external archives from .chezmoiexternal.yaml
chhome home4/user
chezmoi apply --force
cmp $HOME/.dir/dir/file golden/dir/file
readlink $HOME/.dir/dir/symlink file
[!windows] readlink $HOME/.dir/dir/symlink file
! exists $HOME/.dir/file

-- archive/dir/file --
Expand Down
43 changes: 43 additions & 0 deletions internal/cmd/testdata/scripts/externalencrypted.txt
@@ -0,0 +1,43 @@
[!exec:gpg] skip 'gpg not found in $PATH'

mkgpgconfig

# use chezmoi's encryption to encrypt a file and an archive
exec tar czf $HOME/archive.tar.gz archive
chezmoi add --encrypt $HOME${/}.file $HOME${/}archive.tar.gz
mkdir www
cp $CHEZMOISOURCEDIR/encrypted_dot_file.asc www/.file.asc
cp $CHEZMOISOURCEDIR/encrypted_archive.tar.gz.asc www/archive.tar.gz.asc

httpd www

# test that chezmoi reads encrypted external files and archives
chhome home2/user
mkdir $CHEZMOICONFIGDIR
cp home/user/.config/chezmoi/chezmoi.toml $CHEZMOICONFIGDIR
chezmoi apply --force
cmp $HOME/.file golden/.file
cmp $HOME/.dir/file golden/dir/file

-- archive/dir/file --
# contents of dir/file
-- golden/.file --
# contents of .file
-- golden/dir/file --
# contents of dir/file
-- home/user/.file --
# contents of .file
-- home2/user/.local/share/chezmoi/.chezmoiexternal.json --
{
".file": {
"type": "file",
"url": "{{ env "HTTPD_URL" }}/.file.asc",
"encrypted": true
},
".dir": {
"type": "archive",
"url": "{{ env "HTTPD_URL" }}/archive.tar.gz.asc",
"encrypted": true,
"stripComponents": 2
}
}

0 comments on commit 0346200

Please sign in to comment.