diff --git a/.gitignore b/.gitignore index eaa2bf6e..f4c2ae30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.pls_cache *.o *~ Makefile @@ -22,6 +23,7 @@ Makefile.in /src/hydra-eval-jobs/hydra-eval-jobs /src/root/static/bootstrap /src/root/static/js/flot +/tests /doc/manual/images /doc/manual/manual.html /doc/manual/manual.pdf @@ -41,3 +43,4 @@ stamp-h1 src/hydra-evaluator/hydra-evaluator src/hydra-queue-runner/hydra-queue-runner src/root/static/fontawesome/ +src/root/static/bootstrap*/ diff --git a/README.md b/README.md index 6139edad..8156f22f 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ After making your changes, verify the test suite still passes. After following t ``` $ nix-shell $ make check +$ # Or, to run a single test, use: +$ yath test ./t/foo/bar.t ``` ### JSON API diff --git a/doc/manual/src/SUMMARY.md b/doc/manual/src/SUMMARY.md index b3a58a96..ab60d0d8 100644 --- a/doc/manual/src/SUMMARY.md +++ b/doc/manual/src/SUMMARY.md @@ -2,6 +2,7 @@ - [Introduction](introduction.md) - [Installation](installation.md) +- [Configuration](configuration.md) - [Creating and Managing Projects](projects.md) - [Hydra jobs](./jobs.md) - [Using the external API](api.md) diff --git a/doc/manual/src/configuration.md b/doc/manual/src/configuration.md new file mode 100644 index 00000000..170287da --- /dev/null +++ b/doc/manual/src/configuration.md @@ -0,0 +1,128 @@ +Configuration +============= + +This chapter is a collection of configuration snippets for different +scenarios. + +Including files +--------------- + +`hydra.conf` supports Apache-style includes. This is **IMPORTANT** +because that is how you keep your **secrets** out of the **Nix store**. +Hopefully this got your attention 😌 + +This: +``` + +NixOS = Bearer gha-secret😱secret😱secret😱 + +``` +should **NOT** be in `hydra.conf`. + +`hydra.conf` is rendered in the Nix store and is therefore world-readable. + +Instead, the above should be written to a file outside the Nix store by +other means (manually, using Nixops' secrets feature, etc) and included +like so: +``` +Include /run/keys/hydra/github_authorizations.conf +``` + +Serving behind reverse proxy +---------------------------- + +To serve hydra web server behind reverse proxy like *nginx* or *httpd* +some additional configuration must be made. + +Edit your `hydra.conf` file in a similar way to this example: + +```conf +using_frontend_proxy 1 +base_uri example.com +``` + +`base_uri` should be your hydra servers proxied URL. If you are using +Hydra nixos module then setting `hydraURL` option should be enough. + +If you want to serve Hydra with a prefix path, for example +[http://example.com/hydra]() then you need to configure your reverse +proxy to pass `X-Request-Base` to hydra, with prefix path as value. For +example if you are using nginx, then use configuration similar to +following: + + server { + listen 433 ssl; + server_name example.com; + .. other configuration .. + location /hydra/ { + + proxy_pass http://127.0.0.1:3000; + proxy_redirect http://127.0.0.1:3000 https://example.com/hydra; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Request-Base /hydra; + } + } + +Statsd Configuration +-------------------- + +By default, Hydra will send stats to statsd at `localhost:8125`. Point Hydra to a different server via: + +``` + + host = alternative.host + port = 18125 + +``` + +Using LDAP as authentication backend (optional) +----------------------------------------------- + +Instead of using Hydra\'s built-in user management you can optionally +use LDAP to manage roles and users. + +The `hydra-server` accepts the environment variable +*HYDRA\_LDAP\_CONFIG*. The value of the variable should point to a valid +YAML file containing the Catalyst LDAP configuration. The format of the +configuration file is describe in the +[*Catalyst::Authentication::Store::LDAP* +documentation](https://metacpan.org/pod/Catalyst::Authentication::Store::LDAP#CONFIGURATION-OPTIONS). +An example is given below. + +Roles can be assigned to users based on their LDAP group membership +(*use\_roles: 1* in the below example). For a user to have the role +*admin* assigned to them they should be in the group *hydra\_admin*. In +general any LDAP group of the form *hydra\_some\_role* (notice the +*hydra\_* prefix) will work. + + credential: + class: Password + password_field: password + password_type: self_check + store: + class: LDAP + ldap_server: localhost + ldap_server_options.timeout: 30 + binddn: "cn=root,dc=example" + bindpw: notapassword + start_tls: 0 + start_tls_options + verify: none + user_basedn: "ou=users,dc=example" + user_filter: "(&(objectClass=inetOrgPerson)(cn=%s))" + user_scope: one + user_field: cn + user_search_options: + deref: always + use_roles: 1 + role_basedn: "ou=groups,dc=example" + role_filter: "(&(objectClass=groupOfNames)(member=%s))" + role_scope: one + role_field: cn + role_value: dn + role_search_options: + deref: always diff --git a/doc/manual/src/installation.md b/doc/manual/src/installation.md index 79d261cb..626d8286 100644 --- a/doc/manual/src/installation.md +++ b/doc/manual/src/installation.md @@ -163,102 +163,3 @@ processes that come into play: All three processes must be running for Hydra to be fully functional, though it\'s possible to temporarily stop any one of them for maintenance purposes, for instance. - -Serving behind reverse proxy ----------------------------- - -To serve hydra web server behind reverse proxy like *nginx* or *httpd* -some additional configuration must be made. - -Edit your `hydra.conf` file in a similar way to this example: - -```conf -using_frontend_proxy 1 -base_uri example.com -``` - -`base_uri` should be your hydra servers proxied URL. If you are using -Hydra nixos module then setting `hydraURL` option should be enough. - -If you want to serve Hydra with a prefix path, for example -[http://example.com/hydra]() then you need to configure your reverse -proxy to pass `X-Request-Base` to hydra, with prefix path as value. For -example if you are using nginx, then use configuration similar to -following: - - server { - listen 433 ssl; - server_name example.com; - .. other configuration .. - location /hydra/ { - - proxy_pass http://127.0.0.1:3000; - proxy_redirect http://127.0.0.1:3000 https://example.com/hydra; - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Request-Base /hydra; - } - } - -Statsd Configuration --------------------- - -By default, Hydra will send stats to statsd at `localhost:8125`. Point Hydra to a different server via: - -``` - - host = alternative.host - port = 18125 - -``` - -Using LDAP as authentication backend (optional) ------------------------------------------------ - -Instead of using Hydra\'s built-in user management you can optionally -use LDAP to manage roles and users. - -The `hydra-server` accepts the environment variable -*HYDRA\_LDAP\_CONFIG*. The value of the variable should point to a valid -YAML file containing the Catalyst LDAP configuration. The format of the -configuration file is describe in the -[*Catalyst::Authentication::Store::LDAP* -documentation](https://metacpan.org/pod/Catalyst::Authentication::Store::LDAP#CONFIGURATION-OPTIONS). -An example is given below. - -Roles can be assigned to users based on their LDAP group membership -(*use\_roles: 1* in the below example). For a user to have the role -*admin* assigned to them they should be in the group *hydra\_admin*. In -general any LDAP group of the form *hydra\_some\_role* (notice the -*hydra\_* prefix) will work. - - credential: - class: Password - password_field: password - password_type: self_check - store: - class: LDAP - ldap_server: localhost - ldap_server_options.timeout: 30 - binddn: "cn=root,dc=example" - bindpw: notapassword - start_tls: 0 - start_tls_options - verify: none - user_basedn: "ou=users,dc=example" - user_filter: "(&(objectClass=inetOrgPerson)(cn=%s))" - user_scope: one - user_field: cn - user_search_options: - deref: always - use_roles: 1 - role_basedn: "ou=groups,dc=example" - role_filter: "(&(objectClass=groupOfNames)(member=%s))" - role_scope: one - role_field: cn - role_value: dn - role_search_options: - deref: always diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index d77c3dba..6d85f3a9 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -48,6 +48,11 @@ __PACKAGE__->config( file($ENV{'HYDRA_LDAP_CONFIG'}) ) : undef }, + 'Plugin::ConfigLoader' => { + driver => { + 'General' => \%Hydra::Config::configGeneralOpts + } + }, 'Plugin::PrometheusTiny' => { include_action_labels => 1, }, diff --git a/src/lib/Hydra/Config.pm b/src/lib/Hydra/Config.pm new file mode 100644 index 00000000..a681f1c0 --- /dev/null +++ b/src/lib/Hydra/Config.pm @@ -0,0 +1,5 @@ +package Hydra::Config; + +our %configGeneralOpts = (-UseApacheInclude => 1, -IncludeAgain => 1, -IncludeRelative => 1); + +1; diff --git a/src/lib/Hydra/Helper/Nix.pm b/src/lib/Hydra/Helper/Nix.pm index 51509c73..5e055e4f 100644 --- a/src/lib/Hydra/Helper/Nix.pm +++ b/src/lib/Hydra/Helper/Nix.pm @@ -5,6 +5,7 @@ use Exporter; use File::Path; use File::Basename; use Config::General; +use Hydra::Config; use Hydra::Helper::CatalystUtils; use Hydra::Model::DB; use Nix::Store; @@ -41,11 +42,9 @@ my $hydraConfig; sub getHydraConfig { return $hydraConfig if defined $hydraConfig; my $conf = $ENV{"HYDRA_CONFIG"} || (Hydra::Model::DB::getHydraPath . "/hydra.conf"); + my %opts = (%Hydra::Config::configGeneralOpts, -ConfigFile => $conf); if (-f $conf) { - my %h = new Config::General( -ConfigFile => $conf - , -UseApacheInclude => 1 - , -IncludeAgain => 1 - )->getall; + my %h = new Config::General(%opts)->getall; $hydraConfig = \%h; } else { diff --git a/t/Config/include.t b/t/Config/include.t new file mode 100644 index 00000000..3e57b3d4 --- /dev/null +++ b/t/Config/include.t @@ -0,0 +1,26 @@ +use strict; +use Setup; + +my %ctx = test_init( + use_external_destination_store => 0, + hydra_config => "include foo.conf" +); + +write_file($ctx{'tmpdir'} . "/foo.conf", q| + + include bar.conf + +|); + +write_file($ctx{'tmpdir'} . "/bar.conf", q| + bar = baz +|); + +require Hydra::Helper::Nix; +use Test2::V0; + +is(Hydra::Helper::Nix::getHydraConfig(), { + foo => { bar => "baz" } +}, "Nested includes work."); + +done_testing; diff --git a/t/lib/Setup.pm b/t/lib/Setup.pm index 151e3d61..800c90eb 100644 --- a/t/lib/Setup.pm +++ b/t/lib/Setup.pm @@ -9,7 +9,7 @@ use File::Basename; use Cwd qw(abs_path getcwd); our @ISA = qw(Exporter); -our @EXPORT = qw(test_init hydra_setup nrBuildsForJobset queuedBuildsForJobset +our @EXPORT = qw(test_init hydra_setup write_file nrBuildsForJobset queuedBuildsForJobset nrQueuedBuildsForJobset createBaseJobset createJobsetWithOneInput evalSucceeds runBuild sendNotifications updateRepository captureStdoutStderr); @@ -49,19 +49,16 @@ sub test_init { $ENV{'NIX_CONF_DIR'} = "$dir/nix/etc/nix"; make_path($ENV{'NIX_CONF_DIR'}); my $nixconf = "$ENV{'NIX_CONF_DIR'}/nix.conf"; - open(my $fh, '>', $nixconf) or die "Could not open file '$nixconf' $!"; - print $fh "sandbox = false\n"; - print $fh $opts{'nix_config'} || ""; - close $fh; - + my $nix_config = "sandbox = false\n" . $opts{'nix_config'}; + write_file($nixconf, $nix_config); $ENV{'HYDRA_CONFIG'} = "$dir/hydra.conf"; - open(my $fh, '>', $ENV{'HYDRA_CONFIG'}) or die "Could not open file '" . $ENV{'HYDRA_CONFIG'}. " $!"; + my $hydra_config = $opts{'hydra_config'} || ""; if ($opts{'use_external_destination_store'} // 1) { - print $fh "store_uri = file:$dir/nix/dest-store\n" + $hydra_config = "store_uri = file:$dir/nix/dest-store\n" . $hydra_config; } - print $fh $opts{'hydra_config'} || ""; - close $fh; + + write_file($ENV{'HYDRA_CONFIG'}, $hydra_config); $ENV{'NIX_LOG_DIR'} = "$dir/nix/var/log/nix"; $ENV{'NIX_REMOTE_SYSTEMS'} = ''; @@ -82,6 +79,13 @@ sub test_init { ); } +sub write_file { + my ($path, $text) = @_; + open(my $fh, '>', $path) or die "Could not open file '$path' $!"; + print $fh $text || ""; + close $fh; +} + sub captureStdoutStderr { # "Lazy"-load Hydra::Helper::Nix to avoid the compile-time # import of Hydra::Model::DB. Early loading of the DB class