Multiple Teams

Every pipeline and one-off build belongs to a team.

A team is simply a conceptual owner and a separate namespace for data in Concourse (pipelines, builds, etc.) with its own auth configuration. A team may map to GitHub oAuth, basic auth, UAA auth, or whatever other auth methods we add in the future.

Importantly, a team does not have "users" added to and removed from it; it's only a configuration of auth methods, which themselves may provide user info that we add semantics to in the future (i.e. which GitHub user you authorized as).

main: the magical mystery team

Out of the box, Concourse comes with a single team called main.

The main team is an admin team, meaning it can create and update other teams. Currently there is no way to promote a team to become an admin team, so main is a special-case.

The main team is configured as part of the deployment itself as flags passed to the ATC. Changes to these flags take effect whenever the ATC starts up. This is so that you can't get locked out, and so that you can have Concourse sanely configured as soon as it comes up.

Consult either web --help for a concourse binary deployment or bosh.io for a BOSH deployment to configure the main team.

Creating Teams

Once you've logged in as the main team with fly, you can run set-team to create or update other teams:

$ fly set-team -n my-team \
    --basic-auth-username ci \
    --basic-auth-password changeme

Once the team has been created, you can use login to log in:

$ fly login -n my-team

Any newly configured pipelines (via set-pipeline) and one-off builds (via execute) will be owned by the authorized team. Commands that list content will be scoped to the current team by default, such as pipelines and builds. The web UI should reflect the same state.

Newly configured pipelines are hidden by default, meaning other teams and unauthorized visitors cannot view them. To make them publicly viewable, see Pipeline and build visibility.

Configuring Auth

Continuous Integration servers often contain a considerable number of secrets to let them access source code and deploy applications. It is important that those secrets remain well guarded. Concourse provides options for both authentication and authorization to give you control over who can access your server and how much they can see.

If you access your Concourse server over the public internet then all of the options below are useless without using TLS to secure your connection.

The authentication methods for teams are determined by flags passed to set-team, except for the main team, which is configured as part of the deployment.

Any number of the following providers can be enabled at any one time. Users will be given a choice when logging in as to which one they would like to use.

Basic Authentication

HTTP Basic authentication is the simplest of the authentication mechanisms. It has good support in both browsers and command line tools. It provides a single set of credentials for all users of the system.

You can configure basic auth for a team via the --basic-auth-username and --basic-auth-password flags:

$ fly set-team -n my-team \
    --basic-auth-username foo \
    --basic-auth-password bar

GitHub oAuth

A Concourse server can authenticate against GitHub to take advantage of their permission model and other security improvements in their infrastructure. There are a number of steps you need to take to enable this.

Configuring the GitHub oAuth for our own team of engineers at Pivotal may look something like:

$ fly set-team -n concourse \
    --github-auth-client-id $CLIENT_ID \
    --github-auth-client-secret $CLIENT_SECRET \
    --github-auth-team concourse/Pivotal

Configuring the callback URL

First you need to create an OAuth application on GitHub.

The name, home page, and description don't matter but should be set to something that lets users trust their origin. The Authorization callback URL should be the external URL of your Concourse server with /auth/github/callback appended. For example, Concourse's own CI server's callback URL is https://ci.concourse.ci/auth/github/callback.

Configuring the Client

You will be given a Client ID and a Client Secret for your new application. These will then be passed to the following flags:

  • --github-auth-client-id=CLIENT_ID

  • --github-auth-client-secret=CLIENT_SECRET

If you're configuring GitHub Enterprise, you'll also need to set the following flags:

  • --github-auth-auth-url=https://github.example.com/login/oauth/authorize

  • --github-auth-token-url=https://github.example.com/login/oauth/access_token

  • --github-auth-api-url=https://github.example.com/api/v3/

Make sure the API URL ends with a slash!

Authorizing GitHub users, teams, and organizations

You're now able to set the organizations, teams, and individual users who should have access to your server.

This is done by passing the following flags:

--github-auth-user=LOGIN

Authorize an individual user.

--github-auth-team=ORG/TEAM NAME

Authorize an team's members within an organization.

--github-auth-organization=ORG

Authorize an entire organization's members.

These flags can be specified multiple times to permit multiple levels of access.

UAA/CF oAuth

The --uaa-auth-* flags allow you to authorize members of a particular space in a CF deployment. In the future we'll make this generic to UAA and request scopes.

Configuring the UAA client

You'll first need to configure a client for Concourse with your UAA. The callback URL will be the external URL of your Concourse server with /auth/oauth/callback appended. For example, Concourse's own CI server's callback URL would be https://ci.concourse.ci/auth/oauth/callback.

The client should look something like this, under uaa.clients:

concourse:
  id: my-client-id
  secret: my-client-secret
  scope: cloud_controller.read
  authorized-grant-types: "authorization_code,refresh_token"
  access-token-validity: 3600
  refresh-token-validity: 3600
  redirect-uri: https://concourse.example.com/auth/uaa/callback

Configuring the team

Configuring auth for my team's space in a CF installation may look something like:

$ fly set-team -n my-cf-team \
    --uaa-auth-client-id $CLIENT_ID \
    --uaa-auth-client-secret $CLIENT_SECRET \
    --uaa-auth-auth-url https://login.my-cf.com/oauth/authorize \
    --uaa-auth-token-url https://login.my-cf.com/oauth/token \
    --uaa-auth-cf-url https://api.my-cf.com \
    --uaa-auth-cf-ca-cert ./path/to/cf-ca-cert.crt \
    --uaa-auth-cf-space 31d89184-35b8-4660-a8ab-a7f72cc24435

The 31d89184-35b8-4660-a8ab-a7f72cc24435 value above is the GUID of the space whose members will be authorized.

Generic oAuth

The --generic-oauth-* flags configure a generic oAuth provider which performs no additional verification about the individual user signing in by default. It should only be used with internal auth systems in this way. If it were used to configure Google or Twitter oAuth, for example, it would permit just about every person on the internet to create pipelines. It'd be mighty generous. If you need verification, make sure you are using the --generic-oauth-scope flag.

Configuring the oAuth client

You'll first need to configure the client with your oAuth provider. The callback URL will be the external URL of your Concourse server with /auth/oauth/callback appended. For example, Concourse's own CI server's callback URL would be https://ci.concourse.ci/auth/oauth/callback.

Configuring the team

Configuring generic oAuth for X company's internal oAuth service may look something like:

$ fly set-team -n my-team-in-x \
    --generic-oauth-display-name 'X' \
    --generic-oauth-client-id $CLIENT_ID \
    --generic-oauth-client-secret $CLIENT_SECRET \
    --generic-oauth-auth-url https://oauth.example.com/o/oauth2/auth \
    --generic-oauth-token-url https://oauth.example.com/o/oauth2/token

You can also pass arbitrary params, for example a scope to request, like so:

$ fly set-team -n my-team-in-x \
    --generic-oauth-display-name 'X' \
    --generic-oauth-client-id $CLIENT_ID \
    --generic-oauth-client-secret $CLIENT_SECRET \
    --generic-oauth-auth-url https://oauth.example.com/o/oauth2/auth \
    --generic-oauth-token-url https://oauth.example.com/o/oauth2/token \
    --generic-oauth-auth-url-param scope:read

The --generic-oauth-auth-url-param flag can be specified multiple times to configure multiple params, in the form of KEY:VALUE.

If you need verification of users, you can choose to require users have a particular scope with --generic-oauth-scope:

$ fly set-team -n my-team-in-x \
    --generic-oauth-display-name 'X' \
    --generic-oauth-client-id $CLIENT_ID \
    --generic-oauth-client-secret $CLIENT_SECRET \
    --generic-oauth-auth-url https://oauth.example.com/o/oauth2/auth \
    --generic-oauth-token-url https://oauth.example.com/o/oauth2/token \
    --generic-oauth-scope concourse.main

Caveats

Given that this is a catch-all provider, there may be things that go wrong that we can't really document in great detail. If things aren't working, try flailing until it works.

For example, some providers seem to expect values like client_id and client_secret to be specified as query params on the token URL. Our oAuth2 library sends these values as the request payload POSTed to the endpoint instead. In this case you could try tacking the query params on to the end of the token URL like so:

$ fly set-team -n my-team-in-x \
    --generic-oauth-display-name 'X' \
    --generic-oauth-client-id $CLIENT_ID \
    --generic-oauth-client-secret $CLIENT_SECRET \
    --generic-oauth-auth-url https://oauth.example.com/o/oauth2/auth \
    --generic-oauth-token-url \
      "https://oauth.example.com/o/oauth2/token?client_secret=$CLIENT_SECRET" \
    --generic-oauth-auth-url-param scope:some-scope

Pretty janky but it works.

Future providers

We've written the authentication layer of Concourse to be easily extendable. Adding a new OAuth provider is as simple as adding a few interface implementations and feeding them any information they need from the command line.

We'd be interested in hearing about any providers with which you'd like to integrate.

Pipeline and build visibility

Every newly configured pipeline is hidden to anyone but the pipeline's team. To make a pipeline publicly viewable, both by other teams and unauthenticated users, see expose-pipeline.

Even with a pipeline exposed, all build logs are hidden by default. This is because CI jobs are prone to leaking credentials and other...unsavory information. After you've determined that a job's builds should be safe for public consumption, you can set public: true on the job in your pipeline.

Caveats

At present, teams only provide trusted multi-tenancy. This means it should be used for cases where you know and trust who you're allowing access into your Concourse cluster.

There are a few reasons it'd be a bad idea to do otherwise:

  • Any team can run privileged builds. A bad actor in the mix could easily use this to harm your workers and your cluster.

    In the future, we'll probably have this as a flag on a team, indicating whether they're permitted to run privileged builds.

  • There are no networking restrictions in place, and traffic to and from the workers is currently unencrypted and unauthorized. Anyone could run a task that does horrible things to your worker's containers, possibly stealing sensitive information.

    This can be remedied with configuration specified on Garden to restrict access to the internal network, but this is not detailed in our docs, and we'll probably want to find a better answer than configuration in the future.