Skip to content

Commit

Permalink
Add GitHub OAuth config
Browse files Browse the repository at this point in the history
Signed-off-by: Sayali Gaikawad <gaiksaya@amazon.com>
  • Loading branch information
gaiksaya committed Nov 25, 2024
1 parent 79a5e39 commit eb12990
Show file tree
Hide file tree
Showing 13 changed files with 877 additions and 217 deletions.
66 changes: 41 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- [Dev Deployment](#dev-deployment)
- [Executing Optional Tasks](#executing-optional-tasks)
- [SSL Configuration](#ssl-configuration)
- [Setup OpenId Connect (OIDC) via Federate](#setup-openid-connect-oidc-via-federate)
- [Setup Authentication using OpenId Connect (OIDC) or GitHub Authentication](#setup-authentication-using-openid-connect-oidc-or-github-authentication)
- [Restricting Server Access](#restricting-server-access)
- [Data Retention](#data-retention)
- [Add environment variable](#add-environment-variables)
Expand Down Expand Up @@ -81,20 +81,20 @@ $aws secretsmanager put-secret-value \

### Executing Optional Tasks
#### Construct Props
| Name | Type | Description |
|------------------------------------------------------------------|:---------|:-----------------------------------------------------------------------------------------|
| [useSsl](#ssl-configuration) <required> | boolean | Should the Jenkins use https |
| [runWithOidc](#setup-openid-connect-oidc-via-federate)<required> | boolean | Should an OIDC provider be installed on Jenkins |
| [restrictServerAccessTo](#restricting-server-access) <required> | Ipeer | Restrict jenkins server access |
| [ignoreResourcesFailures](#ignore-resources-failure) | boolean | Additional verification during deployment and resource startup |
| [adminUsers](#setup-openid-connect-oidc-via-federate) | string[] | List of users with admin access during initial deployment |
| [additionalCommands](#runnning-additional-commands) | string | Additional logic that needs to be run on Master Node. The value has to be path to a file |
| [dataRetention](#data-retention) | boolean | Do you want to retain jenkins jobs and build history |
| [agentAssumeRole](#assume-role) | string | IAM role ARN to be assumed by jenkins agent nodes |
| [envVarsFilePath](#add-environment-variables) | string | Path to file containing env variables in the form of key value pairs |
| [macAgent](#mac-agents) | boolean | Add mac agents to jenkins |
| [useProdAgents](#use-production-agents) | boolean | Should jenkins server use production agents |
| [enableViews](#enable-views) | boolean | Adds Build, Test, Release and Misc views to Jenkins Dashboard . Defaults to false |
| Name | Type | Description |
|-----------------------------------------------------------|:---------|:-----------------------------------------------------------------------------------------|
| [useSsl](#ssl-configuration) <required> | boolean | Should the Jenkins use https |
| [restrictServerAccessTo](#restricting-server-access) <required> | Ipeer | Restrict jenkins server access |
| [authType](#setup-authentication-using-openid-connect-oidc-or-github-authentication) | string | Authentication type for Jenkins login. Acceptable values: github, oidc, default |
| [ignoreResourcesFailures](#ignore-resources-failure) | boolean | Additional verification during deployment and resource startup |
| [adminUsers](#setup-openid-connect-oidc-via-federate) | string[] | List of users with admin access during initial deployment |
| [additionalCommands](#runnning-additional-commands) | string | Additional logic that needs to be run on Master Node. The value has to be path to a file |
| [dataRetention](#data-retention) | boolean | Do you want to retain jenkins jobs and build history |
| [agentAssumeRole](#assume-role) | string | IAM role ARN to be assumed by jenkins agent nodes |
| [envVarsFilePath](#add-environment-variables) | string | Path to file containing env variables in the form of key value pairs |
| [macAgent](#mac-agents) | boolean | Add mac agents to jenkins |
| [useProdAgents](#use-production-agents) | boolean | Should jenkins server use production agents |
| [enableViews](#enable-views) | boolean | Adds Build, Test, Release and Misc views to Jenkins Dashboard . Defaults to false |
#### SSL Configuration
1. Locate the secret manager arns in the ci-config-stack outputs
1. Update the secret value ([see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)) for the `certContentsSecret` with the certificate contents
Expand All @@ -112,10 +112,14 @@ $aws secretsmanager put-secret-value \
1. `cdk deploy OpenSearch-CI-Dev -c useSsl=true -c runWithOidc=true`
1. Continue with [next steps](#dev-deployment)

#### Setup OpenId Connect (OIDC) via Federate
1. Locate the secret manager arns in the ci-config-stack outputs
1. Update the secret value ([see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)) for the `OIDCClientIdSecret` with the credentials as json as follows:
1. JSON format
#### Setup Authentication using OpenId Connect (OIDC) or GitHub Authentication
There are 3 types of authentication that can be used with this setup. The code modifies the [securityRealm](https://www.jenkins.io/doc/book/security/managing-security/#enabling-security) setting of jenkins.
1. **Default**: Adopts whatever is mentioned in the [initial jenkins.yaml](resources/baseJenkins.yaml) file. Defaults to 'Jenkins Own User Database'.
1. **OpenID Connect**: User any OpenID Connect provider to the jenkins.
Steps:
1. Locate the secret manager arns in the ci-config-stack outputs
1. Update the secret value ([see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)) for the `authConfigValueSecret` with the credentials as json as follows:
JSON format
```
{
"clientId": "example_id",
Expand All @@ -126,16 +130,28 @@ $aws secretsmanager put-secret-value \
"userInfoServerUrl": "https://example.com/userinfo"
}
```
1. Command Eg: [see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)
1. **GitHub Authentication**: Use GitHub as Authentication mechanism for jenkins. This set up uses [github-oauth](https://plugins.jenkins.io/github-oauth/) plugin.
Steps:
1. Create a GitHub OAuth app by following the instructions mentioned on the [plugin info page](https://plugins.jenkins.io/github-oauth/).
1. Locate the secret manager arns in the ci-config-stack outputs
1. Update the secret value ([see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)) for the `authConfigValueSecret` with the credentials as json as follows:
JSON format
```
{
"clientID": "example_id",
"clientSecret": "example_password"
}
```
1. Command to update secrets Eg: [see docs](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/put-secret-value.html)
```
$aws secretsmanager put-secret-value \
--secret-id MyTestDatabaseSecret_or_ARN \
--secret-string file://mycreds.json_or_value
```
1. Add additional `adminUsers` for role based authentication according to your needs, see [CIStackProps](./lib/ci-stack.ts) for details.
```
1. Add additional `adminUsers` for role based authentication according to your needs, see [CIStackProps](./lib/ci-stack.ts) for details.
1. Run with parameter with one of the following (refer [this](#ssl-configuration) for value of `useSsL`) -
1. `npm run cdk deploy OpenSearch-CI-Dev -- -c runWithOidc=true -c useSsl=true` or,
1. `cdk deploy OpenSearch-CI-Dev -c runWithOidc=true -c useSsl=true`
1. `npm run cdk deploy OpenSearch-CI-Dev -- -c authType=oidc/github/default -c useSsl=true` or,
1. `cdk deploy OpenSearch-CI-Dev -c authType=oidc/github/default -c useSsl=true`
1. Continue with [next steps](#dev-deployment)
#### Restricting Server Access
Expand All @@ -144,7 +160,7 @@ You need to restrict access to your jenkins endpoint (load balancer). Here's how
1. Using command line as below:
```
npm run cdk synth OpenSearch-CI-Dev -- -c useSsl=false -c runWithOidc=false -c serverAccessType=ipv4 -c restrictServerAccessTo=10.10.10.10/32
npm run cdk synth OpenSearch-CI-Dev -- -c useSsl=false -c serverAccessType=ipv4 -c restrictServerAccessTo=10.10.10.10/32
```
Below values are allowed:
| serverAccessType| restrictServerAccessTo|
Expand Down
1 change: 1 addition & 0 deletions bin/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ const ciStack = new CIStack(app, `OpenSearch-CI-${defaultEnv}`, {

const ciCdnStack = new CiCdnStack(app, `OpenSearch-CI-Cdn-${defaultEnv}`, {});
ciCdnStack.addDependency(ciStack);
ciStack.addDependency(ciConfigStack);
13 changes: 7 additions & 6 deletions lib/ci-config-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class CIConfigStack extends Stack {

static readonly REDIRECT_URL_SECRET_EXPORT_VALUE: string = 'redirectUrlSecret';

static readonly OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE: string = 'OIDCConfigValueSecret';
static readonly AUTH_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE: string = 'authConfigValueSecret';

static readonly CASC_RELOAD_TOKEN_SECRET_EXPORT_VALUE: string = 'casc';

Expand All @@ -43,9 +43,10 @@ export class CIConfigStack extends Stack {
const redirectUrlSecret = new Secret(this, 'redirectUrl', {
description: 'Redirect url for Jenkins',
});
const OIDCConfigValuesSecret = new Secret(this, 'OIDCConfigValues', {
description: 'OIDC params in JSON format',
const authConfigValuesSecret = new Secret(this, 'authConfigValues', {
description: 'Auth credentials in JSON format',
});

const CascReloadTokenValuesSecret = new Secret(this, 'CascReloadTokenValue', {
description: 'Reload token (password) required for configuration as code plugin',
});
Expand Down Expand Up @@ -75,9 +76,9 @@ export class CIConfigStack extends Stack {
exportName: CIConfigStack.REDIRECT_URL_SECRET_EXPORT_VALUE,
});

new CfnOutput(this, 'OIDCConfigValuesSecret', {
value: OIDCConfigValuesSecret.secretArn,
exportName: CIConfigStack.OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE,
new CfnOutput(this, 'authConfigValuesSecret', {
value: authConfigValuesSecret.secretArn,
exportName: CIConfigStack.AUTH_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE,
});

new CfnOutput(this, 'cascSecretValue', {
Expand Down
25 changes: 13 additions & 12 deletions lib/ci-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import { JenkinsWAF } from './security/waf';
export interface CIStackProps extends StackProps {
/** Should the Jenkins use https */
readonly useSsl?: boolean;
/** Should an OIDC provider be installed on Jenkins. */
readonly runWithOidc?: boolean;
/** Type of login mechanism to adopt */
readonly authType?:string;
/** Restrict jenkins access to */
readonly restrictServerAccessTo?: IPeer;
/** Additional verification during deployment and resource startup. */
Expand All @@ -54,7 +54,7 @@ export interface CIStackProps extends StackProps {
readonly useProdAgents?: boolean;
}

function getServerAccess(serverAccessType: string, restrictServerAccessTo: string) : IPeer {
function getServerAccess(serverAccessType: string, restrictServerAccessTo: string): IPeer {
if (typeof restrictServerAccessTo === 'undefined') {
throw new Error('restrictServerAccessTo should be specified');
}
Expand Down Expand Up @@ -100,18 +100,19 @@ export class CIStack extends Stack {

const useSsl = useSslParameter === 'true';

const runWithOidcParameter = `${props?.runWithOidc ?? this.node.tryGetContext('runWithOidc')}`;
if (runWithOidcParameter !== 'true' && runWithOidcParameter !== 'false') {
throw new Error('runWithOidc parameter is required to be set as - true or false');
let authType = `${props?.authType ?? this.node.tryGetContext('authType')}`;
if (authType.toString() === 'undefined') {
authType = 'default';
}
if (authType !== 'default' && authType !== 'github' && authType !== 'oidc') {
throw new Error('authType parameter is required to be set as - default, github or oidc');
}

let useProdAgents = `${props?.useProdAgents ?? this.node.tryGetContext('useProdAgents')}`;
if (useProdAgents.toString() === 'undefined') {
useProdAgents = 'false';
}

const runWithOidc = runWithOidcParameter === 'true';

const serverAccessType = this.node.tryGetContext('serverAccessType');
const restrictServerAccessTo = this.node.tryGetContext('restrictServerAccessTo');
const serverAcess = props?.restrictServerAccessTo ?? getServerAccess(serverAccessType, restrictServerAccessTo);
Expand All @@ -124,7 +125,7 @@ export class CIStack extends Stack {
// Setting CfnParameters to record the value in cloudFormation
new CfnParameter(this, 'runWithOidc', {
description: 'If the jenkins instance should use OIDC + federate',
default: runWithOidc,
default: authType,
});

// Setting CfnParameters to record the value in cloudFormation
Expand All @@ -139,7 +140,7 @@ export class CIStack extends Stack {
const importedCertSecretBucketValue = Fn.importValue(`${CIConfigStack.PRIVATE_KEY_SECRET_EXPORT_VALUE}`);
const importedArnSecretBucketValue = Fn.importValue(`${CIConfigStack.CERTIFICATE_ARN_SECRET_EXPORT_VALUE}`);
const importedRedirectUrlSecretBucketValue = Fn.importValue(`${CIConfigStack.REDIRECT_URL_SECRET_EXPORT_VALUE}`);
const importedOidcConfigValuesSecretBucketValue = Fn.importValue(`${CIConfigStack.OIDC_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE}`);
const importedAuthConfigValuesSecretBucketValue = Fn.importValue(`${CIConfigStack.AUTH_CONFIGURATION_VALUE_SECRET_EXPORT_VALUE}`);
const certificateArn = Secret.fromSecretCompleteArn(this, 'certificateArn', importedArnSecretBucketValue.toString());
const importedReloadPasswordSecretsArn = Fn.importValue(`${CIConfigStack.CASC_RELOAD_TOKEN_SECRET_EXPORT_VALUE}`);
const listenerCertificate = ListenerCertificate.fromArn(certificateArn.secretValue.toString());
Expand Down Expand Up @@ -186,9 +187,9 @@ export class CIStack extends Stack {
sslCertChainArn: importedContentsChainBucketValue.toString(),
sslCertPrivateKeyContentsArn: importedCertSecretBucketValue.toString(),
redirectUrlArn: importedRedirectUrlSecretBucketValue.toString(),
oidcCredArn: importedOidcConfigValuesSecretBucketValue.toString(),
authCredsSecretsArn: importedAuthConfigValuesSecretBucketValue.toString(),
useSsl,
runWithOidc,
authType,
failOnCloudInitError: props?.ignoreResourcesFailures,
adminUsers: props?.adminUsers,
agentNodeSecurityGroup: this.securityGroups.agentNodeSG.securityGroupId,
Expand Down
121 changes: 121 additions & 0 deletions lib/compute/auth-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

export class AuthConfig {
private static readonly adminRolePermissions: string[] = [
'Overall/Administer',
'Overall/Read',
'Job/Move',
'Job/Build',
'Job/Read',
'Job/Delete',
'Job/Create',
'Job/Discover',
'Job/Cancel',
'Job/Configure',
'Job Config History/DeleteEntry',
'Job/Workspace',
'Credentials/Delete',
'Credentials/ManageDomains',
'Credentials/Update',
'Credentials/View',
'Credentials/Create',
'Manage ownership/Nodes',
'Manage ownership/Jobs',
'Agent/Configure',
'Agent/Create',
'Agent/Build',
'Agent/Provision',
'Agent/Connect',
'Agent/Delete',
'Agent/Disconnect',
'Run/Replay',
'Run/Delete',
'Run/Update',
'View/Delete',
'View/Read',
'View/Create',
'View/Configure',
'SCM/Tag',
];

private static readonly readOnlyRolePermissions: string[] = [
'Overall/Read',
'Job/Read',
'View/Read',
];

public static addOidcConfigToJenkinsYaml(yamlObject: any, authType: string, admins?: string[]): any {
const jenkinsYaml: any = yamlObject;
let adminUsers: string[] = ['admin'];
const readOnlyUsers: string[] = ['anonymous'];

if (admins) {
adminUsers = adminUsers.concat(admins);
}

const oidcConfig: { [x: string]: any; } = {
oic: {
clientId: 'clientId',
clientSecret: 'clientSecret',
authorizationServerUrl: 'http://localhost',
wellKnownOpenIDConfigurationUrl: 'wellKnownOpenIDConfigurationUrl',
tokenServerUrl: 'tokenServerUrl',
userInfoServerUrl: 'userInfoServerUrl',
disableSslVerification: false,
userNameField: 'sub',
escapeHatchEnabled: false,
logoutFromOpenidProvider: true,
postLogoutRedirectUrl: '',
scopes: 'openid',
escapeHatchSecret: 'random',
},
};

const githubAuthConfig: { [x: string]: any; } = {
github: {
githubWebUri: 'https://github.com',
githubApiUri: 'https://api.github.com',
clientID: 'clientID',
clientSecret: 'clientSecret',
oauthScopes: 'read:org,user:email',
},
};

const rolesAndPermissions: { [x: string]: any; } = {
roleBased: {
roles: {
global: [{
entries: adminUsers.map((user) => ({ user })),
name: 'admin',
pattern: '.*',
permissions: AuthConfig.adminRolePermissions
,
},
{
entries: readOnlyUsers.map((user) => ({ user })),
name: 'read',
pattern: '.*',
permissions: AuthConfig.readOnlyRolePermissions,
},

],
},
},
};

jenkinsYaml.jenkins.authorizationStrategy = rolesAndPermissions;

if (authType === 'github') {
jenkinsYaml.jenkins.securityRealm = githubAuthConfig;
} else {
jenkinsYaml.jenkins.securityRealm = oidcConfig;
}
return jenkinsYaml;
}
}
Loading

0 comments on commit eb12990

Please sign in to comment.