GithubHelp home page GithubHelp logo

zeus's Introduction

Zeus CI Server

Introduction

Zeus is a no-fuss production quality CI server for Nix projects.

Mainstream CI providers are not well suited for Nix projects. Because they run on ephemeral containers that are created fresh each time you build, you have to either install all your dependencies on every build or create your own image to build from. Since Nix manages ALL dependencies from the lowest level system library all the way up, this means copying a lot of data. Even if you have everything cached, this still makes builds slower. If you don't have perfect caching, it's even worse.

After trying many different providers we concluded that you really need dedicated build machines for Nix projects. This allows you to get blazing fast builds because all the build artifacts are kept in the build machine's nix store across builds. You get perfect dependency caching out of the box with minimal rebuilding or transferring cached data over the network.

Features

Zeus makes it drop dead simple to get blazing fast CI for Nix projects today by providing the following features:

  • Automatically sets up webhooks for both GitHub and GitLab repositories.
  • All builds made available from the Zeus machine as a Nix binary cache so all your infrastructure from development to deployment rebuilds as little as possible.
  • Support for pushing build outputs to a Nix cache hosted on S3.
  • Organizations with closed source software can limit server and cache access to specific IP addresses / subnets.
  • Sends build status to GitHub allowing Zeus to control the merging of pull requests.

Roadmap

The following features are high priority and planned for the very near future:

  • Ability to distribute builds to multiple build machines
  • Proper support for merge requests with a safe solution to the problem of anyone on the internet having arbitrary code execution on non-ephemeral build servers.
  • User authentication and authorization

Other tasks

  • Fully automated DB migrations

Installing

Setting up the Zeus Binary Cache

To set up the Zeus binary cache and avoid building everything, add the following to your /etc/nix/nix.conf:

substituters = https://zeus.mightybyte.net https://cache.nixos.org/
trusted-public-keys = zeus.mightybyte.net-1:LXywRTa8NaNw7x32JJOncRMxFk2I36K6Tf6ac8VRnho= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=

Alternatively, if you don't want to add it to your nix.conf, you can add these command line arguments to the nix-build lines in the install instructions below (this only works if your user is listed in the trusted-users section of nix.conf):

--option trusted-public-keys "zeus.mightybyte.net-1:LXywRTa8NaNw7x32JJOncRMxFk2I36K6Tf6ac8VRnho= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" --option substituters "https://zeus.mightybyte.net https://cache.nixos.org/"

Deploying to a Remote Server with Obelisk

The easiest way to deploy Zeus is with Obelisk. To deploy with Obelisk, you will need to have NixOS installed on the computer you are deploying to. You also need to deploy from another Linux computer. You cannot use obelisk to deploy from macOS or Windows.

If you already have the Reflex / Obelisk nix cache set up or if you don't mind waiting longer for it to build, you can install Obelisk with this command:

nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz -iA command

Otherwise you may want to refer to the Obelisk documentation here:

https://github.com/obsidiansystems/obelisk#installing-obelisk

Once you have Obelisk installed, run the following commands. Be sure to substitute correct values for your setup on the ob deploy init command. You need to put your real email address so you can renew the LetsEncrypt certs when they expire.

git clone https://github.com/mightybyte/zeus.git
cd zeus
mkdir ob-deploy-info
ob deploy init ob-deploy-info --ssh-key path/to/ssh-key.pem  --hostname ci.example.com --route https://ci.example.com --admin-email [email protected]
cd ob-deploy-info
ob deploy push

Zeus CI should now be running on your server! Point your browser at https://ci.example.com and you're off to the races.

Running Zeus Without Obelisk

git clone https://github.com/mightybyte/zeus.git
cd zeus
nix-build -A exe -o result-exe
mkdir -p config/common
echo http://localhost:8000 > config/common/route
result-exe/backend -p 8000 +RTS -N

NOTE: The nix-build command on line 3 requires that your user be listed in the trusted-users section in your /etc/nix/nix.conf. Alternatively, If you add the s3://zeus-ci cache and its public key to your /etc/nix/nix.conf, then you can leave off the --option bits.

This runs the Zeus server on port 8000. Point your browser at http://localhost:8000 and you should see the Zeus web UI.

In order for Zeus to work this way, the server it is running on needs to be reachable from GitHub or GitLab and you need to tell the frontend what that address is. To do that, change the address in the file config/common/route to something like this:

http://ci.example.com

Don't forget to make sure that the port specified in config/common/route matches the port that you're running the server on.

Running Zeus Locally

If you are running Zeus behind NAT or in some other situation where it's not reachable from the internet (perhaps if you're contributing to Zeus and testing on your local machine for instance), you can get it to work by setting up a reverse SSH tunnel to a machine that does have a public address. For example, if you are running it as described above and you have SSH access to a machine at ci.example.com with a public IP, you can set up an SSH tunnel as follows:

ssh -R '*:8000:localhost:8000' [email protected]

You need to have this SSH tunnel open when you create the Repo in the Zeus UI, and also have it open whenever you want pushes to the repository to trigger builds on Zeus.

With this setup you need to set config/common/route to this:

http://localhost:8000

This is because the frontend is running locally and needs to open websocket connections to localhost to connect to the server.

The astute reader will notice a problem here. This is the correct address for the frontend to use for its websocket connection, but it is not the correct address for GitHub/GitLab to use to connect to Zeus. If you have a public IP these two addresses are the same, but if you are running locally these two addresses are different. In this situation you can tell Zeus the public address that GitHub/GitLab should use by specifying webhookBaseUrl in the config/backend/settings as described in the "Configuring the Backend" section below.

Setup

Configuring the Backend

The Zeus backend has a few configuration options that you can specify in the file config/backend/settings.json on the server. This file should contain server settings in JSON format. Here is an example settings file:

{
  "webhookBaseUrl": "http://ci.example.com:8000",
  "ipWhitelist": [
    "52.201.220.249/32",
    "176.9.155.62/32"
  ]
}

If you're deploying with Obelisk as described above, you should create this file in ob-deploy-info/config/backend/settings.json after you do ob deploy init but before ob deploy push.

webhookBaseUrl is an optional field described above in the "Running Zeus Locally" section. ipWhitelist is a list of CIDR blocks describing IP addresses that are allowed to connect to the server. If the list is empty, then the server will be accessible from anywhere. This feature is primarily intended as a quick and dirty way to provide access control until we get proper authentication and access control implemented.

Setting up an Account

Once you have gotten Zeus running through one of the above methods, you need to connect it to your GitHub/GitLab account and set up repositories that you want it to build. Point your browser at the frontend address from above (http://localhost:8000, etc). The first thing you need to do is set up an account. Click the "Accounts" tab, and then click "Connect Account", then type the name of your account.

After you do this you need to create a personal access token. You can do this as follows:

GitHub

  1. Go to https://github.com/settings/tokens
  2. Click "Generate new token"
  3. Type something descriptive in the Note field like "Zeus CI"
  4. Check "repo" and all its sub-items
  5. Check "admin:repo_hook" and all its sub-items
  6. Click "Generate token"

GitLab

  1. Go to https://gitlab.com/profile/personal_access_tokens
  2. Type something descriptive in the Name field like "Zeus CI"
  3. Check "api", "read_user", and "read_repository"
  4. Click "Create personal access token"

Once you've done this the access token will be displayed and you can copy it to your clipboard. Then go back to Zeus and paste this into the "Access Token" field. Then select the appropriate value for "Provider" and click "Connect Account".

Setting up a repo

Now that you have an account connected, click on the "Repos" tab and click "Add Repository". Select the appropriate account from the "Access Account" dropdown. Here are some examples of how to set the "Repo Namespace" and "Repo Name" fields.

If your repo URL is "https://github.com/alice/my-repo", the namespace will be "alice" and the name will be "my-repo". The values would be the same for GitHub or GitLab.

If your repo URL is "https://gitlab.com/acme-corp/frontend-team/foo-frontend", the namespace will be "acme-corp/frontend-team" and the name will be "foo-frontend".

Fill out the rest of the form as appropriate, then click "Add Repo" and you should be good to go! Zeus will now run a build every time someone pushes to your repo.

Setting up as S3 cache

  1. Create an Amazon S3 bucket to store your cache
  2. Go to the bucket's "Properties" tab, click on "Static website hosting", and check "Use this bucket to host a website".
  3. Go to "Permissions" -> "Block public access", click "Edit", and uncheck all the boxes.
  4. In "Permissions" -> "Bucket Policy", set the following policy:
{
    "Version": "2012-10-17",
    "Id": "DirectReads",
    "Statement": [
        {
            "Sid": "AllowDirectReads",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::my-nix-cache-bucket-name",
                "arn:aws:s3:::my-nix-cache-bucket-name/*"
            ]
        }
    ]
}

...substituting my-nix-cache-bucket-name with the name of your bucket.

  1. Go to the "Caches" tab, click "Add Cache", fill in the form with information for your cache, and click "Connect Cache".
  2. Create a new repo in the "Repos" tab and select the S3 cache you just created.
  3. To enable this cache on your machine, edit /etc/nix/nix.conf and add the Zeus Cache Public Key (viewable in the Settings tab) to trusted-public-keys. Then add one of the following addresses to the substituters line:
  • s3://my-nix-cache-bucket-name
  • http://my-nix-cache.example.com
  • https://my-nix-cache.example.com

If you want anyone to use the s3:// prefix, you will have to include the s3:ListBucket action as shown in the above policy. But if you want to use this cache to serve private code, you definitely don't want to allow people to list the objects in the bucket because then they will be able to download anything in your cache. If you use the http:// or https:// prefix, then you can remove the ListBucket action and it will still work. In that case you have to be sure to set up a domain name to point to the S3 bucket.

NOTE: The first time you do a build that pushes to your S3 cache, it will probably take several hours to upload the full transitive closure of all the dependencies. After that first time, it will be much faster because most of the dependencies will already be there.

Migrations

Fully automated migration handling isn't planned until core CI features have been implemented. In the meantime we will put all the necessary SQL code for migrations in migrations.md. If you encounter a migration that cannot be automated, the backend server will fail to start. The easiest way to solve this is to delete zeus.db and restart the server. Or, if you want to preserve your existing Zeus database, run the migrations from migrations.db manually.

zeus's People

Contributors

mightybyte avatar jonathanlorimer avatar jmininger avatar korayal avatar ryantrinkle avatar

Stargazers

Jasha Sommer-Simpson avatar Matthieu Coudron avatar June avatar Guillaume Desforges avatar Melg Eight avatar Jon Roberts avatar Tim Deeb-Swihart avatar sam bacha avatar Karl Marrett avatar Christian Klinger avatar Bailey avatar Matt Schreiber avatar Stefan Junker avatar Michael Mercier avatar Ryan Lahfa avatar Rob Vermaas avatar Patrik Stutz avatar Francis De Brabandere avatar Emmanuel Denloye-Ito avatar Dmitry Pogodin avatar Alexey Zabelin avatar Sergei Dolgov avatar Pasqualino 'Titto' Assini avatar  avatar Antti Holvikari avatar Lîm Tsú-thuàn avatar Steven Shaw avatar Anatolii Prylutskyi avatar Zach Schuermann avatar Yevhen Smolanka avatar Gareth Smith avatar Armando Santos avatar Nicole Wren avatar  avatar Fabien Bourgeois avatar Edmund Wu avatar Jann Müller avatar Travis A. Everett avatar Kamil Chmielewski avatar Medson Oliveira avatar  avatar space-shell avatar Félix avatar Dhruv Dang avatar Ian-Woo Kim avatar Florian Peter avatar Daniel Fullmer avatar Thales Macedo Garitezi avatar Sora Morimoto avatar Tristan de Cacqueray avatar ajs124 avatar Josh Miller avatar Oleksii Filonenko avatar paluh avatar  avatar  avatar Masashi Fujita avatar Donna avatar Kirill Kuznetsov avatar Sergey B avatar Thomas Honeyman avatar Tatsuya Hirose avatar Ramin Honary avatar Ari Becker avatar Andrejs Agejevs avatar Tim Kersey avatar James Brock avatar Alexander Barbosa avatar Akira Komamura avatar Ivan avatar Enis Bayramoğlu avatar GuangTao Zhang avatar Abby Henríquez Tejera avatar Benjamin Staffin avatar Drew Hess avatar Yorick avatar Divam avatar Mrinal avatar Anupam <|> अनुपम avatar Sridhar Ratnakumar avatar Mitchell Dalvi Rosen avatar  avatar Daniel Kahlenberg avatar Haisheng avatar Daniel Mendler avatar  avatar Alexander Biehl avatar Vaibhav Sagar avatar  avatar Schell Carl Scivally avatar Evan Relf avatar

Watchers

 avatar Sergei Dolgov avatar Utku Demir avatar Mitchell Dalvi Rosen avatar Simon Jakobi avatar Kerstin avatar  avatar  avatar

zeus's Issues

Cache is out of date?

It looks like the project cache is not up to date, because nix-build is compiling a bunch of stuff.

FWIW, I did specify the cache declaratively in my configuration.nix:

image

Unable to create a repo without s3 cache

Not sure if this was intended. When trying to create a repo, the Add Repo button remains disabled. I note that 'S3 Cache' is the only field unfilled, which is intentional on my part as I don't intend to use an external cache at all.

image

Secure building of third-party pull/merge requests

Automatic building of third-party pull requests is a potential security problem because it gives anyone in the world arbitrary code execution on the Zeus build machine and consequently the ability to but artifacts into the Nix cache that Zeus populates. See for example the warning in GitHub's self-hosted runner documentation. The nix build sandbox protections are probably not sufficient to solve this security issue.

Currently Zeus is secure against this because it only builds when someone pushes to the repository. But it is inconvenient because when a third party opens a pull request Zeus does not trigger a build. There are a few different approaches we could take to fix this:

  1. Do nothing and require a trusted user to review the pull request and push it to the repo after verifying the code is ok.
  2. Require some kind of confirmation from someone who has permissions to the repo. This could be implemented in the Zeus web UI or potentially triggered by some kind of action on GitHub such as a pull request review, comment, etc.
  3. Build third-party PRs automatically, but in a special way such as on an ephemeral build machine spun up for a one-time build and without pushing build artifacts to the cache.

Approach #2 would be very easy if it was triggered by some kind of action on GitHub. The difficulty there is figuring out what that action should be so that is both easy and secure. If it is done in the Zeus UI, it would be a bit more work, probably 1-5 days of dev time.

Approach #3 would be the best end-user experience because it would generate a CI build status without requiring any effort from the maintainers, but it is significantly more complicated and requires infrastructure for multiple Zeus build machines.

Figure out a way to auto-set the nix path

In GitLab by @mightybyte on Mar 27, 2020, 14:37

In lieu of a proper solution, the current approach allows the user to set this in Settings. But that's still not a great experience. We need to figure out an approach that is suitably robust across different platforms / operating systems / etc.

Remove the SSH clone method

In GitLab by @mightybyte on Jul 1, 2019, 17:20

The HTTP clone method should now be more robust and easier to use since we already have the user's public access token. SSH cloning is probably unnecessary.

Implement simple IP-based access control

In GitLab by @mightybyte on Jun 24, 2019, 24:08

This is a stopgap to get things to a state appropriate for real world industrial use before we get to the bigger medium-term task of a proper auth system.

Access control should not apply to the /hook endpoints because they are already secured with a secret random token.

Implement Gitea API for build status

Gitea is a lightweight, self-hosted Github-like app which, unlike Gitlab, does not come with a builtin CI, but it does have the proper endpoints for setting build status and creating webhooks.

Wouldn't it be nice if Zeus was Gitea-compatible?

systemd cannot start the backend due to cache signing key

In GitLab by @MichaelXavier on Aug 1, 2019, 18:26

After deploying to an EC2 machine running the latest nixos image (ami ami-0efc58fb70ae9a217 via https://nixos.org/nixos/download.html on us-east-1). nginx seems to start but the backend repeatedly fails while generating the cache signing key. From /var/lib/backend, tailing the logs I get the following again and again:

==> backend.err <==
backend: zeus-cache-key.sec: renameFile:renamePath:rename: permission denied (Permission denied)

==> backend.out <==
read settings: BackendSettings {_beSettings_webhookBaseUrl = Nothing, _beSettings_ipWhitelist = [Cidr {_cidrIp = 2274085441, _cidrMask = 32}]}
Generating cache signing key
/nix/store/mzrh3y44sqrxrbb0xba5li9y838ndyxs-nix-2.1.3/bin/nix-store --generate-binary-cache-key zeus.soostonelabs.com-1 zeus-cache-key.sec zeus-cache-key.pub

Both files are owned by backend, which seems to be the user that ought to be running this. zeus-cache-key.sec is chmodded to 600. Running the command that it is trying to run doesn't seem to complain:

su -s /run/current-system/sw/bin/bash -c '/nix/store/mzrh3y44sqrxrbb0xba5li9y838ndyxs-nix-2.1.3/bin/nix-store --generate-binary-cache-key zeus.soostonelabs.com-1 zeus-cache-key.sec zeus-cache-key.pub' backend exits with 0.

Add license

In GitLab by @evanrelf on Mar 11, 2020, 14:33

I'm interested in comparing zeus to Hydra for CI at $WORK, but I need clarification on the license situation. If the project is proprietary, and you're not interested in releasing it under an open source license, that's fine too as long as it's made clear.

Thanks.

Handle when builds are blocked by other nix builds

If a build is blocked by another nix-build that is already in progress, there may be some situations where the build won't be started properly. In some cases stopping and restarting Zeus seems to restart the pending builds.

libstore: nar-info-disk-cache.cc: Assertion `hashPart == storePathToHash(info->path)' failed

Investigation under the original ticket obsidiansystems/obelisk#768 has lead us to believe that the issue is related to Zeus, not obelisk, so I'm creating an issue here.

Error following the tutorial:

$ nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz -iA command
installing 'obelisk-command-0.8.0.0'
nix-env: src/libstore/nar-info-disk-cache.cc:233: nix::NarInfoDiskCacheImpl::upsertNarInfo(const string&, const string&, std::shared_ptr<nix::ValidPathInfo>)::<lambda()>: Assertion `hashPart == storePathToHash(info->path)' failed.
[1]    2798858 abort (core dumped)  nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz  

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.