Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a generate PKI command #917

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Collection of additional tools to make airgap easier
* [zarf](zarf.md) - DevSecOps Airgap Toolkit
* [zarf tools archiver](zarf_tools_archiver.md) - Compress/Decompress tools for Zarf packages
* [zarf tools clear-cache](zarf_tools_clear-cache.md) - Clears the configured git and image cache directory
* [zarf tools gen-pki](zarf_tools_gen-pki.md) - Generates a Certificate Authority and PKI chain of trust for the given host
* [zarf tools get-git-password](zarf_tools_get-git-password.md) - Returns the push user's password for the Git server
* [zarf tools monitor](zarf_tools_monitor.md) - Launch K9s tool for managing K8s clusters
* [zarf tools registry](zarf_tools_registry.md) - Collection of registry commands provided by Crane
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ Compress/Decompress tools for Zarf packages

* [zarf tools](zarf_tools.md) - Collection of additional tools to make airgap easier
* [zarf tools archiver compress](zarf_tools_archiver_compress.md) - Compress a collection of sources based off of the destination file extension
* [zarf tools archiver decompress](zarf_tools_archiver_decompress.md) - Decompress an archive (package) to a specified location.
* [zarf tools archiver decompress](zarf_tools_archiver_decompress.md) - Decompress an archive (package) to a specified location

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Compress a collection of sources based off of the destination file extension

```
zarf tools archiver compress [SOURCES] [ARCHIVE] [flags]
zarf tools archiver compress {SOURCES} {ARCHIVE} [flags]
```

### Options
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
## zarf tools archiver decompress

Decompress an archive (package) to a specified location.
Decompress an archive (package) to a specified location

```
zarf tools archiver decompress [ARCHIVE] [DESTINATION] [flags]
zarf tools archiver decompress {ARCHIVE} {DESTINATION} [flags]
```

### Options
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## zarf tools gen-pki

Generates a Certificate Authority and PKI chain of trust for the given host

```
zarf tools gen-pki {HOST} [flags]
```

### Options

```
-h, --help help for gen-pki
--sub-alt-name stringArray Specify Subject Alternative Names for the certificate
```

### Options inherited from parent commands

```
-a, --architecture string Architecture for OCI images
-l, --log-level string Log level when running Zarf. Valid options are: warn, info, debug, trace (default "info")
--no-log-file Disable log file creation
--no-progress Disable fancy UI progress bars, spinners, logos, etc
--tmpdir string Specify the temporary directory to use for intermediate files
--zarf-cache string Specify the location of the Zarf cache directory (default "~/.zarf-cache")
```

### SEE ALSO

* [zarf tools](zarf_tools.md) - Collection of additional tools to make airgap easier

33 changes: 30 additions & 3 deletions src/cmd/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import (
"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/internal/k8s"
"github.com/defenseunicorns/zarf/src/internal/message"
"github.com/defenseunicorns/zarf/src/internal/pki"
k9s "github.com/derailed/k9s/cmd"
craneCmd "github.com/google/go-containerregistry/cmd/crane/cmd"
"github.com/mholt/archiver/v3"
"github.com/spf13/cobra"
)

var subAltNames []string

var toolsCmd = &cobra.Command{
Use: "tools",
Aliases: []string{"t"},
Expand All @@ -28,7 +31,7 @@ var archiverCmd = &cobra.Command{
}

var archiverCompressCmd = &cobra.Command{
Use: "compress [SOURCES] [ARCHIVE]",
Use: "compress {SOURCES} {ARCHIVE}",
Aliases: []string{"c"},
Short: "Compress a collection of sources based off of the destination file extension",
Args: cobra.MinimumNArgs(2),
Expand All @@ -42,9 +45,9 @@ var archiverCompressCmd = &cobra.Command{
}

var archiverDecompressCmd = &cobra.Command{
Use: "decompress [ARCHIVE] [DESTINATION]",
Use: "decompress {ARCHIVE} {DESTINATION}",
Aliases: []string{"d"},
Short: "Decompress an archive (package) to a specified location.",
Short: "Decompress an archive (package) to a specified location",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
sourceArchive, destinationPath := args[0], args[1]
Expand Down Expand Up @@ -104,6 +107,27 @@ var clearCacheCmd = &cobra.Command{
if err := os.RemoveAll(config.GetAbsCachePath()); err != nil {
message.Fatalf("Unable to clear the cache driectory %s: %s", config.GetAbsCachePath(), err.Error())
}
message.SuccessF("Successfully cleared the cache from %s", config.GetAbsCachePath())
},
}

var generatePKICmd = &cobra.Command{
Use: "gen-pki {HOST}",
Aliases: []string{"pki"},
Short: "Generates a Certificate Authority and PKI chain of trust for the given host",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
pki := pki.GeneratePKI(args[0], subAltNames...)
if err := os.WriteFile("tls.ca", pki.CA, 0644); err != nil {
message.Fatalf(err, "Failed to write the CA file: %s", err.Error())
}
if err := os.WriteFile("tls.crt", pki.Cert, 0644); err != nil {
message.Fatalf(err, "Failed to write the Certificate file: %s", err.Error())
}
if err := os.WriteFile("tls.key", pki.Key, 0600); err != nil {
message.Fatalf(err, "Failed to write the Key file: %s", err.Error())
}
message.SuccessF("Successfully created a chain of trust for %s", args[0])
},
}

Expand All @@ -117,6 +141,9 @@ func init() {
toolsCmd.AddCommand(clearCacheCmd)
clearCacheCmd.Flags().StringVar(&config.CommonOptions.CachePath, "zarf-cache", config.ZarfDefaultCachePath, "Specify the location of the Zarf artifact cache (images and git repositories)")

toolsCmd.AddCommand(generatePKICmd)
generatePKICmd.Flags().StringArrayVar(&subAltNames, "sub-alt-name", []string{}, "Specify Subject Alternative Names for the certificate")

archiverCmd.AddCommand(archiverCompressCmd)
archiverCmd.AddCommand(archiverDecompressCmd)

Expand Down
8 changes: 4 additions & 4 deletions src/internal/pki/pki.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@ const org = "Zarf Cluster"
const validFor = time.Hour * 24 * 375

// GeneratePKI create a CA and signed server keypair
func GeneratePKI(host string) types.GeneratedPKI {

func GeneratePKI(host string, dnsNames ...string) types.GeneratedPKI {
results := types.GeneratedPKI{}

ca, caKey, err := generateCA(validFor)
if err != nil {
message.Fatal(err, "Unable to generate the ephemeral CA")
}

hostCert, hostKey, err := generateCert(host, ca, caKey, validFor)
hostCert, hostKey, err := generateCert(host, ca, caKey, validFor, dnsNames...)
if err != nil {
message.Fatalf(err, "Unable to generate the cert for %s", host)
}
Expand Down Expand Up @@ -120,7 +119,7 @@ func generateCA(validFor time.Duration) (*x509.Certificate, *rsa.PrivateKey, err
// generateCert generates a new certificate for the given host using the
// provided certificate authority. The cert and key files are stored in
// the provided files.
func generateCert(host string, ca *x509.Certificate, caKey *rsa.PrivateKey, validFor time.Duration) (*x509.Certificate, *rsa.PrivateKey, error) {
func generateCert(host string, ca *x509.Certificate, caKey *rsa.PrivateKey, validFor time.Duration, dnsNames ...string) (*x509.Certificate, *rsa.PrivateKey, error) {
template := newCertificate(validFor)

template.IPAddresses = append(template.IPAddresses, net.ParseIP(config.IPV4Localhost))
Expand All @@ -130,6 +129,7 @@ func generateCert(host string, ca *x509.Certificate, caKey *rsa.PrivateKey, vali
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, host)
template.DNSNames = append(template.DNSNames, dnsNames...)
}

template.Subject.CommonName = host
Expand Down
19 changes: 18 additions & 1 deletion src/test/e2e/00_use_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,22 @@ func TestUseCLI(t *testing.T) {
assert.EqualError(t, err, msg, "Did not receive expected error when reading a directory that should not exist")
}

e2e.cleanFiles(shasumTestFilePath, cachePath, otherTmpPath, pkgName)
// Test generation of PKI
tlsCA := "tls.ca"
tlsCert := "tls.crt"
tlsKey := "tls.key"
stdOut, stdErr, err = e2e.execZarfCommand("tools", "gen-pki", "github.com", "--sub-alt-name", "google.com")
require.NoError(t, err, stdOut, stdErr)
require.Contains(t, stdErr, "Successfully created a chain of trust for github.com")

_, err = os.ReadFile(tlsCA)
require.NoError(t, err)

_, err = os.ReadFile(tlsCert)
require.NoError(t, err)

_, err = os.ReadFile(tlsKey)
require.NoError(t, err)

e2e.cleanFiles(shasumTestFilePath, cachePath, otherTmpPath, pkgName, tlsCA, tlsCert, tlsKey)
}