Skip to content

Commit

Permalink
Merge branch 'master' into 216-schema-precommit
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-mccoy authored Mar 11, 2022
2 parents 60c0439 + 8c3ab91 commit 49cc830
Show file tree
Hide file tree
Showing 21 changed files with 524 additions and 121 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-k3d.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
- name: Build CLI
run: make build-cli-linux
- name: Make Packages
run: make init-package package-example-game package-example-data-injection package-example-gitops-data
run: make init-package package-example-game package-example-data-injection package-example-gitops-data package-example-compose
- name: Run Tests
run: TESTDISTRO=k3d make test-e2e
2 changes: 1 addition & 1 deletion .github/workflows/test-k3s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Build CLI
run: make build-cli-linux
- name: Make Packages
run: make init-package package-example-game package-example-data-injection package-example-gitops-data
run: make init-package package-example-game package-example-data-injection package-example-gitops-data package-example-compose
- name: Run Tests
# NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of go installed in a previous step
run: sudo env "PATH=$PATH" TESTDISTRO=k3s make test-e2e
2 changes: 1 addition & 1 deletion .github/workflows/test-kind.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
- name: Build CLI
run: make build-cli-linux
- name: Make Packages
run: make init-package package-example-game package-example-data-injection package-example-gitops-data
run: make init-package package-example-game package-example-data-injection package-example-gitops-data package-example-compose
- name: Run Tests
run: TESTDISTRO=kind make test-e2e
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ package-example-gitops-data:
package-example-tiny-kafka:
cd examples/tiny-kafka && ../../$(ZARF_BIN) package create --confirm && mv zarf-package-* ../../build/

.PHONY: package-example-compose
package-example-compose:
cd examples/composable-packages && ../../$(ZARF_BIN) package create --confirm && mv zarf-package-* ../../build/

# TODO: This can be cleaned up a little more when `zarf init` is able to provide the path to the `zarf-init.tar.zst`
.PHONY: test-new-e2e
test-e2e: ## Run e2e tests on a KiND cluster. All dependencies are assumed to be built and in the ./build directory
Expand All @@ -97,5 +101,7 @@ test-e2e: ## Run e2e tests on a KiND cluster. All dependencies are assumed to be
@if [ ! -f ./build/zarf-package-gitops-service-data.tar.zst ]; then\
$(MAKE) package-example-gitops-data;\
fi

@if [ ! -f ./build/zarf-package-compose-example.tar.zst ]; then\
$(MAKE) package-example-compose;\
fi
cd test/e2e && cp ../../build/zarf-init.tar.zst . && go test ./... -v -timeout 2400s && rm zarf-init.tar.zst
129 changes: 36 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
# Zarf - Kubernetes Air Gap Buddy
# Zarf - DevSecOps for Air Gap Systems

<img align="right" alt="zarf logo" src=".images/zarf-logo.png" height="256" />

Zarf massively simplifies the setup & administration of kubernetes clusters "across the [air gap](https://en.wikipedia.org/wiki/Air_gap_(networking))".
Zarf massively simplifies the setup & administration of kubernetes clusters, cyber systems & workloads that support DevSecOps "across the [air gap](https://en.wikipedia.org/wiki/Air_gap_(networking))".

It provides a static go binary CLI that can pull, package, and install all the things your clusters need to run. It caches downloads (for speed), hashes packages (for security), and can even _install the kubernetes cluster itself_ if you want it to.
It provides a static go binary (can run anywhere) CLI that can pull, package, and install all the things your clusters need to run along with any necessary resources to standup infrastructure such as Terraform. Zarf also caches downloads (for speed), hashes packages (for security), and can _even install the Kubernetes cluster itself_ if you want it to.

Zarf runs on [a bunch of operating systems](./docs/supported-oses.md) and aims to support configurations ranging from "I want to run one, simple app" to "I need to support & dependency control a _bunch_ of internet-disconnected clusters".

[![asciicast](https://asciinema.org/a/475530.svg)](https://asciinema.org/a/475530)

&nbsp;
## Why is Zarf Needed?
Most of the software ecosystem assumes your systems have access to the internet. The world (for good reasons) has become more and more dependent upon Software as a Service (SaaS), which assumes a robust connection to the internet and a willingness to inherently trust 3rd party providers. Although this makes sense for most of the world, there are certain SECURE systems that must operate either fully disconnected, semi-disconnected, or might need the ability to disconnect in case of emergencies (like while under an active cyber attack). Although only a small percentage of systems, these SECURE systems make up some of the most vital systems in the world, such as Aerospace and Defense, Finance, Healthcare, Energy, Water, Sewage, and many Federal, Local, and State Government systems.

> _This repo is in transition from [Repo1](https://repo1.dso.mil/) by [DoD Platform One](http://p1.dso.mil/) to [Github](https://github.com/defenseunicorns/zarf). See [the announcments post](https://github.com/defenseunicorns/zarf/discussions/1#discussion-3560306) for the latest URLs for this project during this transition._
These SECURE systems need a way to continuously and securely deliver software too. Zarf exists to make it easy for open-source, commercial, and organic software solutions to be delivered to secure and disconnected systems. Although such systems might be small in number, they represent many of the most important systems in the world.

&nbsp;
## Explain Zarf Like I'm Ten(ish)

&nbsp;
Zarf allows you to bundle portions of "the internet" into a single package to be installed later following specific instructions. A Zarf package is really just a single file that includes everything you would need to manage a system or capability while fully disconnected. Think of a disconnected system as a system that always is or sometimes is on airplane mode.

You bring this single file (or package) with you to the system you want to install or update new software onto. The package includes instructions on how to assemble all the pieces of software (components) once on the other side. These instructions are fully "declarative," which means that everything is represented by code and automated vs manual. The hardest part is assembling the declarative package on the connected side. But once everything is packaged up, Zarf makes even massively complex systems easy to install, update, and maintain within disconnected systems.

Such packages also become highly distributable, as they can now run on edge, embedded systems, secure cloud, data centers, or even on a local environment. This is incredibly helpful for organizations that need to integrate and deploy software from multiple secure development environments from a disparate set of development teams into disconnected IT operational environments. Zarf helps ensure that development teams can integrate with the production environment they are deploying to, even if they will never actually touch that environment.

Zarf makes DevSecOps for air gap possible.

&nbsp;
<!--
##########
# This block is about LEARNING TO USE Zarf
Expand Down Expand Up @@ -69,91 +81,6 @@ Zarf runs on [a bunch of operating systems](./docs/supported-oses.md) and aims t
</td>
</tr>
<!-- row end -->

<!-- row start -->
<tr valign="top">
<td>

**Roll Your Own**

_Custom packages_

</td>
<td>

Once you're comfortable with the basic workflow & able to deploy _someone else's_ Zarf deployment packages, it's time to roll your own. Here's how.

</td>
<td>

Coming Soon!

</td>
</tr>
<!-- row end -->

</tbody>
</table>

&nbsp;


<!--
##########
# This block is about expected USECASES & ADMIN of Zarf (in production)
##########
-->
## To understand *the different modes of use*, have a look at...

<table>
<tbody>

<!-- row start: cuz markdown hates html indention -->
<tr valign="top">
<td width="150">

**Simple Applications**

_Appliance Mode_

</td>
<td>

If want to "run a Kubernetes app" but aren't into hand-rolling a cluster just for it, Zarf can help. Here's how, and _why_ you might want to.

</td>
<td>

Coming Soon!

</td>
</tr>
<!-- row end -->

<!-- row start -->
<tr valign="top">
<td>

**Disconnected GitOps**

_The Utility Cluster_

</td>
<td>

Zarf overcomes the "the Air Gap problem" using a Kubernetes cluster (and k8s-native tooling) for the care & feeding of _other k8s clusters_.

Here's how it works and what ops/support looks like.

</td>
<td>

Coming Soon!

</td>
</tr>
<!-- row end -->

</tbody>
</table>

Expand Down Expand Up @@ -233,9 +160,6 @@ Zarf runs on [a bunch of operating systems](./docs/supported-oses.md) and aims t
</tbody>
</table>

&nbsp;


<!--
##########
# This block is about the MINUTIA & UNDERSTANDING WHY Zarf is the way it is
Expand Down Expand Up @@ -335,3 +259,22 @@ Zarf runs on [a bunch of operating systems](./docs/supported-oses.md) and aims t


&nbsp;


## Zarf Nerd Notes

Zarf is written entirely in [go](https://go.dev/), except for a single 400Kb binary for the injector sytem written in [rust](https://www.rust-lang.org/) so we can fit it in a [configmap](https://kubernetes.io/docs/concepts/configuration/configmap/). All assets are bundled together into a single [zstd](https://facebook.github.io/zstd/) tarball on each `zarf package create` operation. On the air gap / offline side `zarf package deploy` extracts the various assets and places them on the filesystem or installs them in the cluster, depending on what the [zarf package](zarf.yaml) says to do. Some important ideas behind zarf:

- All workloads are installed in the cluster via the [Helm SDK](https://helm.sh/docs/topics/advanced/#go-sdk)
- The OCI Registries used are both from [Docker](https://github.com/distribution/distribution)
- Currently the Registry and Git servers _are not HA_, see [#375](https://github.com/defenseunicorns/zarf/issues/376) and [#376](https://github.com/defenseunicorns/zarf/issues/376) for discussion on this
- In order to avoid TLS issues, Zarf binds to `127.0.0.1:319999` on each node as a [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) to allow all nodes to access the pod(s) in the cluster
- Until [#306](https://github.com/defenseunicorns/zarf/pull/306) is merged, during helm install/upgrade a [Helm PostRender](https://helm.sh/docs/topics/advanced/#post-rendering) function is called to mutate images and [ImagePullSecrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) so the deployed resoures use the NodePort binding
- Once [#329](https://github.com/defenseunicorns/zarf/pull/329) is merged, Zarf will use a new injector system to bootstrap a new cluster:
- Get a list images in the cluster
- Attempt to create an ephemeral pod using an image from the lsit
- A small rust binary that is compiled using [musl](https://www.musl-libc.org/) to keep the size the max binary size of ~ 672 KBs is injected into the pod
- The mini zarf registry binary and `docker:2` images are put in a tar archive and split into 512 KB chunks, larger sizes tended to cause latency issues on low-resource control planes
- An init container runs the rust binary to reassabmle and extract the zarf binary and registry image
- The container then starts and runs the zarf binary to host the registry image in an embedded docker registry
- After this the main docker registry chart is deployed, pulls the image from the ephemeral pod and destroys the created configmaps, pod and service
3 changes: 1 addition & 2 deletions assets/scripts/zarf-clean-k3s.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ rm -f /usr/sbin/k3s
rm -f /usr/sbin/ctr
rm -f /usr/sbin/crictl
rm -f /usr/sbin/kubectl
rm -f /usr/sbin/k9s
rm -f /usr/sbin/k3s-remove.sh
rm -f /opt/zarf/k3s-remove.sh
rm -fr zarf-pki

echo -e '\033[0m'
32 changes: 27 additions & 5 deletions cli/cmd/destroy.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package cmd

import (
"errors"
"os"
"regexp"

"github.com/defenseunicorns/zarf/cli/config"
"github.com/defenseunicorns/zarf/cli/internal/helm"
"github.com/defenseunicorns/zarf/cli/internal/message"
"github.com/defenseunicorns/zarf/cli/internal/utils"
"github.com/defenseunicorns/zarf/cli/types"

"github.com/defenseunicorns/zarf/cli/internal/k8s"
"github.com/defenseunicorns/zarf/cli/internal/utils"

"github.com/spf13/cobra"
)
Expand All @@ -20,17 +24,35 @@ var destroyCmd = &cobra.Command{
Aliases: []string{"d"},
Short: "Tear it all down, we'll miss you Zarf...",
Run: func(cmd *cobra.Command, args []string) {
// NOTE: If 'zarf init' failed to deploy the k3s component (or if we're looking at the wrong kubeconfig)
// there will be no zarf-state to load and the struct will be empty. In these cases, if we can find
// the scripts to remove k3s, we will still try to remove a locally installed k3s cluster
state := k8s.LoadZarfState()
_ = os.Remove(".zarf-registry")

if state.ZarfAppliance {
// If Zarf deployed the cluster, burn it all down
// If Zarf deployed the cluster, burn it all down
if state.ZarfAppliance || (state == types.ZarfState{Distro: k8s.DistroIsUnknown}) {
// Check if we have the scripts to destory everything
fileInfo, err := os.Stat(config.ZarfCleanupScriptsPath)
if errors.Is(err, os.ErrNotExist) || !fileInfo.IsDir() {
message.Warnf("Unable to find the folder (%v) which has the scripts to cleanup the cluster. Do you have the right kube-context?\n", config.ZarfCleanupScriptsPath)
return
}

// Run all the scripts!
pattern := regexp.MustCompile(`(?mi)zarf-clean-.+\.sh$`)
scripts := utils.RecursiveFileList("/usr/sbin", pattern)
scripts := utils.RecursiveFileList(config.ZarfCleanupScriptsPath, pattern)
// Iterate over al matching zarf-clean scripts and exec them
for _, script := range scripts {
// Run the matched script
_, _ = utils.ExecCommand(true, nil, script)
_, err := utils.ExecCommand(true, nil, script)
if errors.Is(err, os.ErrPermission) {
message.Warnf("Got a 'permission denied' when trying to execute the script (%v). Are you the right user and/or do you have the right kube-context?\n", script)

// Don't remove scripts we can't execute so the user can try to manually run
continue
}

// Try to remove the script, but ignore any errors
_ = os.Remove(script)
}
Expand Down
6 changes: 6 additions & 0 deletions cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
ZarfConnectLabelName = "zarf.dev/connect-name"
ZarfConnectAnnotationDescription = "zarf.dev/connect-description"
ZarfConnectAnnotationUrl = "zarf.dev/connect-url"

ZarfCleanupScriptsPath = "/opt/zarf"
)

var (
Expand Down Expand Up @@ -116,6 +118,10 @@ func GetComponents() []types.ZarfComponent {
return config.Components
}

func SetComponents(components []types.ZarfComponent) {
config.Components = components
}

func GetBuildData() types.ZarfBuildData {
return config.Build
}
Expand Down
31 changes: 18 additions & 13 deletions cli/internal/packager/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,7 @@ func getValidComponents(allComponents []types.ZarfComponent, requestedComponentN
}
}
} else {
// Present the users with the component details one more time
displayComponent := component
displayComponent.Description = ""
content, _ := yaml.Marshal(displayComponent)
utils.ColorPrintYAML(string(content))
message.Question(fmt.Sprintf("%s: %s", component.Name, component.Description))

// Since no requested components were provided, prompt the user
prompt := &survey.Confirm{
Message: "Deploy this component?",
Default: component.Default,
}
_ = survey.AskOne(prompt, &confirmComponent)
confirmComponent = ConfirmOptionalComponent(component)
}
}

Expand Down Expand Up @@ -150,6 +138,23 @@ func getValidComponents(allComponents []types.ZarfComponent, requestedComponentN
return validComponentsList
}

// Confirm optional component
func ConfirmOptionalComponent(component types.ZarfComponent) (confirmComponent bool) {
displayComponent := component
displayComponent.Description = ""
content, _ := yaml.Marshal(displayComponent)
utils.ColorPrintYAML(string(content))
message.Question(fmt.Sprintf("%s: %s", component.Name, component.Description))

// Since no requested components were provided, prompt the user
prompt := &survey.Confirm{
Message: "Deploy this component?",
Default: component.Default,
}
_ = survey.AskOne(prompt, &confirmComponent)
return confirmComponent
}

// HandleIfURL If provided package is a URL download it to a temp directory
func HandleIfURL(packagePath string, shasum string, insecureDeploy bool) (string, func()) {
// Check if the user gave us a remote package
Expand Down
Loading

0 comments on commit 49cc830

Please sign in to comment.