-
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,017 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Nix | ||
result | ||
result-* | ||
|
||
# Terraform | ||
*.tfstate | ||
*.tfstate.backup | ||
.terraform | ||
.terraform.* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# Terraform and Nix integration | ||
|
||
[![built with nix](https://builtwithnix.org/badge.svg)](https://builtwithnix.org) | ||
|
||
This repository contains a set of Terraform Modules designed to deploy NixOS | ||
machines. These modules are designed to work together and support different | ||
deployment scenarios. | ||
|
||
## What is Terraform? | ||
|
||
[Terraform][terraform] is a tool that allows to declare infrastructures as | ||
code. | ||
|
||
## What is Nix, nixpkgs and NixOS? | ||
|
||
[Nix][nix] is a build system and package manager that allows to manage whole | ||
system configurations as code. nixpkgs is a set of 20k+ packages built with | ||
Nix. NixOS is a Linux distribution built on top of nixpkgs. | ||
|
||
## What is a Terraform Module? | ||
|
||
A Terraform Module refers to a self-contained packages of Terraform | ||
configurations that are managed as a group. This repo contains a collection of | ||
Terraform Modules which can be composed together to create useful | ||
infrastructure patterns. | ||
|
||
## Terraform + Nix vs NixOps | ||
|
||
NixOps is a great tool for personal deployments. It handles a lot of things | ||
like cloud resource creation, machine NixOS bootstrapping and deployment. | ||
|
||
The difficulty is when the cloud resources are not supported by NixOps. It | ||
takes a lot of work to map all the cloud APIs. Compared to NixOps, Terraform | ||
has become an industry standard and has thousands of people contributing new | ||
cloud API mapping all the time. | ||
|
||
Another issue is when sharing the configuration as code with multiple | ||
developers. Both NixOps and Terraform maintain a state file of "known applied" | ||
configuration. Unlike NixOps, Terraform provides facilities to sync and lock | ||
the state file so it's available by other users. | ||
|
||
The approach here is to use Terraform to create all the cloud resources. By | ||
using the `google_image_nixos_custom` module it's possible to pre-build images in | ||
auto-scaling scenarios. Or use a push model similar to NixOps with the generic | ||
`deploy_nixos` module. | ||
|
||
So overall Terraform + Nix is more flexible and scales better. But it's also | ||
more cumbersome to use as it requires to learn two languages instead of one | ||
and the integration between both is also a bit clunky. | ||
|
||
## Terraform Modules | ||
|
||
The list of modules provided by this project: | ||
|
||
* [deploy_nixos](deploy_nixos#readme) - deploy NixOS onto running NixOS | ||
machines | ||
* [google_image_nixos](google_image_nixos#readme) - setup an official GCE | ||
image into a Google Cloud Project. | ||
* [google_image_nixos_custom](google_image_nixos_custom#readme) - build and | ||
deploy a custom GCE image into a Google Cloud Project | ||
|
||
## Examples | ||
|
||
To better understand how these modules can be used together, look into the | ||
[./examples](examples) folder. | ||
|
||
## Future | ||
|
||
* Support other cloud providers. | ||
* Support nixos-infect bootstrapping method. | ||
|
||
Contributions are welcome! | ||
|
||
## Thanks | ||
|
||
Thanks to [Digital Asset][digital-asset] for generously sponsoring this work! | ||
|
||
Thanks to [Tweag][tweag] for enabling this work and the continuous support! | ||
|
||
## License | ||
|
||
This code is released under the Apache 2.0 License. Please see | ||
[LICENSE](LICENSE) for more details. | ||
|
||
Copyright © 2018 Tweag I/O. | ||
|
||
|
||
[digital-asset]: https://www.digitalasset.com/ | ||
[nix]: https://nixos.org/nix/ | ||
[terraform]: https://www.terraform.io | ||
[tweag]: https://www.tweag.io/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# `deploy_nixos` | ||
|
||
A Terraform module that knows how to deploy NixOS onto a target host. | ||
|
||
This allow to describe an infrastructure as code with Terraform and delegate | ||
the machine configuration with NixOS. All directed by Terraform. | ||
|
||
The advantage of this method is that if any of the Nix code changes, the | ||
difference will be detected on the next "terraform plan". | ||
|
||
## Usage | ||
|
||
Either pass a "config" which is a dynamic nixos configuration and a | ||
"config_pwd", or a "nixos_config", a path to a nixos configuration.nix file. | ||
|
||
### Secret handling | ||
|
||
Keys can be passed to the "keys" attribute. Each key will be installed under | ||
`/var/keys/${key}` with the content as the value. | ||
|
||
For services to access one of the keys, add the service user to the "keys" | ||
group. | ||
|
||
The target machine needs `jq` installed prior to the deployment (as part of | ||
the base image). If `jq` is not found it will try to use a version from | ||
`<nixpkgs>`. | ||
|
||
### Disabling sandboxing | ||
|
||
Unfortunately some time it's required to disable the nix sandboxing. To do so, | ||
add `["--option", "sandbox", "false"]` to the "extra_build_args" parameter. | ||
|
||
If that doesn't work, make sure that your user is part of the nix | ||
"trusted-users" list. | ||
|
||
### Non-root `target_user` | ||
|
||
It is possible to connect to the target host using a user that is not `root` | ||
under certain conditions: | ||
|
||
* sudo needs to be installed on the machine | ||
* the user needs password-less sudo access on the machine | ||
|
||
This would typically be provisioned in the base image. | ||
|
||
## Dependencies | ||
|
||
* `nix` | ||
|
||
## Known limitations | ||
|
||
The deployment machine requires Nix with access to a remote builder with the | ||
same system as the target machine. | ||
|
||
Because Nix code is being evaluated at "terraform plan" time, deploying a lot | ||
of machine in the same target will require a lot of RAM. | ||
|
||
All the secrets share the same "keys" group. | ||
|
||
When deploying as non-root, it assumes that passwordless `sudo` is available. | ||
|
||
The target host must already have NixOS installed. | ||
|
||
### config including computed values | ||
|
||
The module doesn't work when `<computed>` values from other resources are | ||
interpolated with the "config" attribute. Because it happens at evaluation | ||
time, terraform will render an empty drvPath. | ||
|
||
see also: | ||
* https://github.com/hashicorp/terraform/issues/16380 | ||
* https://github.com/hashicorp/terraform/issues/16762 | ||
* https://github.com/hashicorp/terraform/issues/17034 | ||
|
||
<!-- terraform-docs-start --> | ||
## Inputs | ||
|
||
| Name | Description | Type | Default | Required | | ||
|------|-------------|:----:|:-----:|:-----:| | ||
| NIX\_PATH | Allow to pass custom NIX_PATH. Ignored if `-`. | string | `-` | no | | ||
| config | NixOS configuration to be evaluated. This argument is required unless 'nixos_config' is given | string | `` | no | | ||
| config\_pwd | Directory to evaluate the configuration in. This argument is required if 'config' is given | string | `` | no | | ||
| extra\_build\_args | List of arguments to pass to the nix builder | list | `<list>` | no | | ||
| extra\_eval\_args | List of arguments to pass to the nix evaluation | list | `<list>` | no | | ||
| keys | A map of filename to content to upload as secrets in /var/keys | map | `<map>` | no | | ||
| nixos\_config | Path to a NixOS configuration | string | `` | no | | ||
| target\_host | DNS host to deploy to | string | - | yes | | ||
| target\_user | SSH user used to connect to the target_host | string | `root` | no | | ||
| triggers | Triggers for deploy | map | `<map>` | no | | ||
|
||
## Outputs | ||
|
||
| Name | Description | | ||
|------|-------------| | ||
| id | random ID that changes on every nixos deployment | | ||
|
||
<!-- terraform-docs-end --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
variable "target_user" { | ||
description = "SSH user used to connect to the target_host" | ||
default = "root" | ||
} | ||
|
||
variable "target_host" { | ||
description = "DNS host to deploy to" | ||
} | ||
|
||
variable "NIX_PATH" { | ||
description = "Allow to pass custom NIX_PATH. Ignored if `-`." | ||
default = "-" | ||
} | ||
|
||
variable "nixos_config" { | ||
description = "Path to a NixOS configuration" | ||
default = "" | ||
} | ||
|
||
variable "config" { | ||
description = "NixOS configuration to be evaluated. This argument is required unless 'nixos_config' is given" | ||
default = "" | ||
} | ||
|
||
variable "config_pwd" { | ||
description = "Directory to evaluate the configuration in. This argument is required if 'config' is given" | ||
default = "" | ||
} | ||
|
||
variable "extra_eval_args" { | ||
description = "List of arguments to pass to the nix evaluation" | ||
type = "list" | ||
default = [] | ||
} | ||
|
||
variable "extra_build_args" { | ||
description = "List of arguments to pass to the nix builder" | ||
type = "list" | ||
default = [] | ||
} | ||
|
||
variable "triggers" { | ||
type = "map" | ||
description = "Triggers for deploy" | ||
default = {} | ||
} | ||
|
||
variable "keys" { | ||
type = "map" | ||
description = "A map of filename to content to upload as secrets in /var/keys" | ||
default = {} | ||
} | ||
|
||
# -------------------------------------------------------------------------- | ||
|
||
locals { | ||
triggers = { | ||
deploy_nixos_drv = "${data.external.nixos-instantiate.result.drv_path}" | ||
deploy_nixos_keys = "${sha256(jsonencode(var.keys))}" | ||
} | ||
|
||
target_system = ["--argstr", "system", "x86_64-linux"] | ||
attr_path = ["--attr", "system"] | ||
} | ||
|
||
# used to detect changes in the configuration | ||
data "external" "nixos-instantiate" { | ||
program = [ | ||
"${path.module}/nixos-instantiate.sh", | ||
"${var.NIX_PATH}", | ||
"${var.config != "" ? var.config : var.nixos_config}", | ||
"${var.config_pwd != "" ? var.config_pwd : "."}", | ||
"${local.target_system}", | ||
"${local.attr_path}", | ||
"${var.extra_eval_args}", | ||
] | ||
} | ||
|
||
resource "null_resource" "deploy_nixos" { | ||
triggers = "${merge(var.triggers, local.triggers)}" | ||
|
||
connection { | ||
type = "ssh" | ||
host = "${var.target_host}" | ||
user = "${var.target_user}" | ||
agent = true | ||
} | ||
|
||
# copy the secret keys to the host | ||
provisioner "file" { | ||
content = "${jsonencode(var.keys)}" | ||
destination = "packed-keys.json" | ||
} | ||
|
||
# FIXME: move this to nixos-deploy.sh | ||
provisioner "file" { | ||
source = "${path.module}/unpack-keys.sh" | ||
destination = "unpack-keys.sh" | ||
} | ||
|
||
# FIXME: move this to nixos-deploy.sh | ||
provisioner "file" { | ||
source = "${path.module}/maybe-sudo.sh" | ||
destination = "maybe-sudo.sh" | ||
} | ||
|
||
provisioner "remote-exec" { | ||
inline = [ | ||
"chmod +x unpack-keys.sh maybe-sudo.sh", | ||
"./maybe-sudo.sh ./unpack-keys.sh ./packed-keys.json", | ||
] | ||
} | ||
|
||
# do the actual deployment | ||
provisioner "local-exec" { | ||
interpreter = [ | ||
"${path.module}/nixos-deploy.sh", | ||
"${data.external.nixos-instantiate.result.drv_path}", | ||
"${var.target_user}@${var.target_host}", | ||
"switch", | ||
"${var.extra_build_args}", | ||
] | ||
|
||
command = "ignoreme" | ||
} | ||
} | ||
|
||
# -------------------------------------------------------------------------- | ||
|
||
output "id" { | ||
description = "random ID that changes on every nixos deployment" | ||
value = "${null_resource.deploy_nixos.id}" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/usr/bin/env bash | ||
# | ||
# Run sudo if required | ||
# | ||
# Usage: ./maybe-sudo.sh <command> [...args] | ||
set -euo pipefail | ||
if [[ "$UID" = 0 ]]; then | ||
exec -- "$@" | ||
else | ||
exec sudo -- "$@" | ||
fi |
Oops, something went wrong.