├── Jenkinsfile
├── docs
└── images
│ ├── qrcode.png
│ ├── alibaba.ak.png
│ ├── alibaba.getak.png
│ ├── jenkins.install.png
│ ├── jenkins.verify.png
│ ├── jenkins.available.png
│ ├── jenkins.credential.png
│ └── jenkins.managePlugin.png
├── src
└── main
│ ├── resources
│ ├── com
│ │ └── alibabacloud
│ │ │ └── credentials
│ │ │ └── plugin
│ │ │ └── auth
│ │ │ ├── AlibabaCredentials
│ │ │ ├── DescriptorImpl
│ │ │ │ └── doCheckSecretKey.stapler
│ │ │ └── credentials.jelly
│ │ │ └── AlibabaSessionTokenCredentials
│ │ │ ├── help-stsTokenDuration_zh_CN.html
│ │ │ ├── help-roleSessionName_zh_CN.html
│ │ │ ├── DescriptorImpl
│ │ │ └── doCheckSecretKey.stapler
│ │ │ ├── help-iamRoleArn_zh_CN.html
│ │ │ ├── help-stsTokenDuration.html
│ │ │ ├── help-iamRoleArn.html
│ │ │ ├── help-roleSessionName.html
│ │ │ └── credentials.jelly
│ └── index.jelly
│ └── java
│ └── com
│ └── alibabacloud
│ └── credentials
│ └── plugin
│ ├── auth
│ ├── AlibabaCloudRamCredentials.java
│ ├── AlibabaPrivateKey.java
│ ├── AlibabaCredentials.java
│ ├── AlibabaKeyPairUtils.java
│ └── AlibabaSessionTokenCredentials.java
│ ├── client
│ └── AlibabaClient.java
│ └── util
│ └── CredentialsHelper.java
├── .gitignore
├── LICENSE
├── README.md
└── pom.xml
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | buildPlugin()
2 |
--------------------------------------------------------------------------------
/docs/images/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/qrcode.png
--------------------------------------------------------------------------------
/docs/images/alibaba.ak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/alibaba.ak.png
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaCredentials/DescriptorImpl/doCheckSecretKey.stapler:
--------------------------------------------------------------------------------
1 | accessKey,value
--------------------------------------------------------------------------------
/docs/images/alibaba.getak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/alibaba.getak.png
--------------------------------------------------------------------------------
/docs/images/jenkins.install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/jenkins.install.png
--------------------------------------------------------------------------------
/docs/images/jenkins.verify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/jenkins.verify.png
--------------------------------------------------------------------------------
/docs/images/jenkins.available.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/jenkins.available.png
--------------------------------------------------------------------------------
/docs/images/jenkins.credential.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/jenkins.credential.png
--------------------------------------------------------------------------------
/docs/images/jenkins.managePlugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/alibabacloud-credentials-plugin/master/docs/images/jenkins.managePlugin.png
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | This plugin integrates Jenkins with Alibaba Cloud credentials
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-stsTokenDuration_zh_CN.html:
--------------------------------------------------------------------------------
1 |
2 | 获得的会话令牌的有效期,以秒为单位。
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | *.classpath
3 | *.settings
4 | *.project
5 | *.iml
6 | *.idea
7 | libraries
8 | target
9 | logs
10 | aliyun-ecs/test-output*
11 | aliyun-ecs/bin
12 | work
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-roleSessionName_zh_CN.html:
--------------------------------------------------------------------------------
1 |
2 | 自定义角色会话名称以区分不同的令牌。 格式应该类似于:“JenKinsTest”。
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/DescriptorImpl/doCheckSecretKey.stapler:
--------------------------------------------------------------------------------
1 | parentAccessKey,iamRoleArn,roleSessionName,stsTokenDuration,value
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-iamRoleArn_zh_CN.html:
--------------------------------------------------------------------------------
1 |
2 | 指定要代入的 IAM 角色的 ARN。 格式应类似于:“acs:iam::123456789012:role/MyIAMRoleName”。
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-stsTokenDuration.html:
--------------------------------------------------------------------------------
1 |
2 | The duration, in seconds, for how long the obtained session token will be valid for.
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-iamRoleArn.html:
--------------------------------------------------------------------------------
1 |
2 | An ARN specifying the IAM role to assume. The format should be something like: "arn:aws:iam::123456789012:role/MyIAMRoleName".
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/help-roleSessionName.html:
--------------------------------------------------------------------------------
1 |
2 | Custom role session name to distinguish between different tokens . The format should be something like: "JenKinsTest".
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/auth/AlibabaCloudRamCredentials.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.auth;
2 |
3 | import com.aliyuncs.auth.AlibabaCloudCredentials;
4 |
5 | /**
6 | * 子账号可以使用该类
7 | */
8 | public interface AlibabaCloudRamCredentials extends AlibabaCloudCredentials {
9 | String getSecretToken();
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaCredentials/credentials.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/resources/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials/credentials.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/auth/AlibabaPrivateKey.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.auth;
2 |
3 | import hudson.util.Secret;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.kohsuke.accmod.Restricted;
6 | import org.kohsuke.accmod.restrictions.NoExternalUse;
7 |
8 | /**
9 | * Created by kunlun.ykl on 2020/8/27.
10 | */
11 | @Slf4j
12 | public class AlibabaPrivateKey {
13 | private final Secret privateKey;
14 | private final String keyPairName;
15 | private final String pfp;
16 |
17 | public AlibabaPrivateKey(String privateKey, String keyPairName) {
18 | this.privateKey = Secret.fromString(privateKey.trim());
19 | this.pfp = AlibabaKeyPairUtils.getPublicFingerprint(privateKey);
20 | this.keyPairName = keyPairName;
21 | }
22 |
23 | public String getPrivateKey() {
24 | return privateKey.getPlainText();
25 | }
26 |
27 | public String getKeyPairName() {
28 | return keyPairName;
29 | }
30 |
31 | @Restricted(NoExternalUse.class)
32 | public Secret getPrivateKeySecret() {
33 | return privateKey;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Alibaba Cloud
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # alibabacloud credentials plugin
2 |
3 | # Introduction
4 | When you need to verify whether AK and SK match when using Alibaba cloud related products,
5 | you can use the alibabcloud credentials Jenkins plugin
6 |
7 |
8 | # Usage
9 |
10 | ### Register account, get AK, SK
11 |
12 | Step 1: before using alibabcloud credentials Jenkins, you need to register an alicloud account
13 | and log in to the alicloud console. ( https://homenew.console.aliyun.com/ )
14 |
15 | Step 2: put the mouse in the user name area at the top right and select access keys from the
16 | shortcut menu that pops up.
17 | 
18 |
19 | Step 3: the system will pop up the security prompt dialog box, click continue to use accessKey,
20 | the page will display accesskeyid and accesskeysecret
21 | 
22 |
23 |
24 | ### Install Jenkins and the plugin
25 |
26 | Once your Jenkins is installed, you can download alibab cloud credentials plugin for use
27 |
28 | * You can navigate to "manage Jenkins" \ > "manage plugins"
29 | 
30 |
31 | * On the plug-in management page, enter "Alibaba cloud credentials" in the query box to query
32 | the plugin
33 | 
34 |
35 | * Scroll to the bottom and select "install without restart"
36 | 
37 |
38 | ### Verify
39 | Once you've installed the plugin, you can verify AK, SK or save it in "manage credentials"
40 | 
41 | 
42 |
43 | # Contact us
44 | * DingTalk Group Number:44723358
45 | * DingTalk Group QR code
46 |
47 | 
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/client/AlibabaClient.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.client;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibabacloud.credentials.plugin.auth.AlibabaSessionTokenCredentials;
5 | import com.alibabacloud.credentials.plugin.util.CredentialsHelper;
6 | import com.aliyuncs.DefaultAcsClient;
7 | import com.aliyuncs.IAcsClient;
8 | import com.aliyuncs.auth.AlibabaCloudCredentials;
9 | import com.aliyuncs.auth.sts.AssumeRoleRequest;
10 | import com.aliyuncs.auth.sts.AssumeRoleResponse;
11 | import com.aliyuncs.ecs.model.v20140526.DescribeKeyPairsRequest;
12 | import com.aliyuncs.ecs.model.v20140526.DescribeKeyPairsResponse;
13 | import com.aliyuncs.ecs.model.v20140526.DescribeRegionsRequest;
14 | import com.aliyuncs.ecs.model.v20140526.DescribeRegionsResponse;
15 | import com.aliyuncs.ecs.model.v20140526.DescribeRegionsResponse.Region;
16 | import com.aliyuncs.exceptions.ClientException;
17 | import com.aliyuncs.http.FormatType;
18 | import com.aliyuncs.http.HttpClientConfig;
19 | import com.aliyuncs.http.ProtocolType;
20 | import com.aliyuncs.profile.DefaultProfile;
21 | import com.aliyuncs.profile.IClientProfile;
22 | import com.google.common.collect.Lists;
23 | import lombok.Getter;
24 | import lombok.extern.slf4j.Slf4j;
25 | import org.apache.commons.collections.CollectionUtils;
26 |
27 | import javax.annotation.Nullable;
28 | import java.util.List;
29 |
30 | /**
31 | * Created by kunlun.ykl on 2020/8/26.
32 | */
33 | @Slf4j
34 | public class AlibabaClient {
35 |
36 | @Getter
37 | private IAcsClient client;
38 | @Getter
39 | private String regionNo;
40 |
41 | public AlibabaClient(AlibabaCloudCredentials credentials, String regionNo, boolean isVpcEnv) {
42 | IClientProfile profile;
43 | if (CredentialsHelper.isSessionTokenCredentials(credentials)) {
44 | profile = DefaultProfile.getProfile(regionNo,
45 | credentials.getAccessKeyId(),
46 | credentials.getAccessKeySecret(),
47 | ((AlibabaSessionTokenCredentials) credentials).getSecretToken());
48 | } else {
49 | profile = DefaultProfile.getProfile(regionNo,
50 | credentials.getAccessKeyId(),
51 | credentials.getAccessKeySecret());
52 | }
53 | // 如果JenkinsMaster是在VPC内网环境下, 则使用内网域名
54 | if(isVpcEnv) {
55 | profile.enableUsingVpcEndpoint();
56 | }
57 | HttpClientConfig clientConfig = HttpClientConfig.getDefault();
58 | clientConfig.setProtocolType(ProtocolType.HTTPS);
59 | clientConfig.setIgnoreSSLCerts(true);
60 | profile.setHttpClientConfig(clientConfig);
61 | this.client = new DefaultAcsClient(profile);
62 | this.regionNo = regionNo;
63 | log.info("AlibabaClient init success. regionNo: {} isVpcEnv: {}", regionNo, isVpcEnv);
64 | }
65 |
66 | public AssumeRoleResponse createAssumeRoleRequest(String iamRoleArn, String roleSessionName, Long stsTokenDuration) throws ClientException {
67 | AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest();
68 | assumeRoleRequest.setMethod(com.aliyuncs.http.MethodType.POST);
69 | assumeRoleRequest.setAcceptFormat(FormatType.JSON);
70 | assumeRoleRequest.setRoleArn(iamRoleArn);
71 | assumeRoleRequest.setRoleSessionName(roleSessionName);
72 | assumeRoleRequest.setDurationSeconds(stsTokenDuration);
73 | return client.getAcsResponse(assumeRoleRequest);
74 | }
75 |
76 | public List describeRegions() {
77 | try {
78 | DescribeRegionsRequest request = new DescribeRegionsRequest();
79 | request.setSysRegionId(regionNo);
80 | DescribeRegionsResponse acsResponse = client.getAcsResponse(request);
81 | if (CollectionUtils.isEmpty(acsResponse.getRegions())) {
82 | return Lists.newArrayList();
83 | }
84 | return acsResponse.getRegions();
85 | } catch (Exception e) {
86 | log.error("describeRegions error.", e);
87 | }
88 | return Lists.newArrayList();
89 | }
90 |
91 | public List describeKeyPairs(@Nullable String keyPairName, @Nullable String pfp) {
92 | try {
93 | DescribeKeyPairsRequest request = new DescribeKeyPairsRequest();
94 | request.setSysRegionId(regionNo);
95 | request.setKeyPairName(keyPairName);
96 | request.setKeyPairFingerPrint(pfp);
97 | DescribeKeyPairsResponse acsResponse = client.getAcsResponse(request);
98 | log.info(JSON.toJSONString(acsResponse));
99 | List keyPairs = acsResponse.getKeyPairs();
100 | if (CollectionUtils.isEmpty(keyPairs)) {
101 | return Lists.newArrayList();
102 | }
103 | return keyPairs;
104 | } catch (Exception e) {
105 | log.error("listKeyPairs error", e);
106 | }
107 | return Lists.newArrayList();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.jenkins-ci.plugins
6 | plugin
7 | 4.4
8 |
9 |
10 | com.alibabacloud.jenkins
11 | alibabacloud-credentials
12 | 1.5-SNAPSHOT
13 | hpi
14 |
15 | 2.190.1
16 | 8
17 | 4.5.30
18 | 4.24.23
19 |
20 | Alibaba Cloud Credentials Plugin
21 |
22 |
23 |
24 | MIT License
25 | https://opensource.org/licenses/MIT
26 |
27 |
28 |
29 | scm:git:https://github.com/jenkinsci/alibabacloud-credentials-plugin.git
30 | scm:git:git@github.com:jenkinsci/alibabacloud-credentials-plugin.git
31 | https://github.com/jenkinsci/alibabacloud-credentials-plugin
32 | HEAD
33 |
34 |
35 |
36 | org.jenkins-ci.plugins
37 | structs
38 | 1.20
39 |
40 |
41 | org.jenkins-ci.plugins
42 | bouncycastle-api
43 | 2.18
44 |
45 |
46 | com.alibaba
47 | fastjson
48 | 1.2.83
49 |
50 |
51 | com.aliyun
52 | aliyun-java-sdk-core
53 | ${aliyun-java-sdk.version}
54 |
55 |
56 | org.jenkins-ci.plugins
57 | credentials
58 | 2.3.11
59 |
60 |
61 | org.projectlombok
62 | lombok
63 | 1.16.12
64 | provided
65 |
66 |
67 | org.slf4j
68 | slf4j-api
69 | 1.7.32
70 | provided
71 |
72 |
73 | com.aliyun
74 | aliyun-java-sdk-ecs
75 | ${aliyun-ecs-java-sdk.version}
76 |
77 |
78 |
79 |
80 |
81 | org.jenkins-ci.main
82 | jenkins-bom
83 | ${jenkins.version}
84 | pom
85 | import
86 |
87 |
88 | org.jenkins-ci.plugins
89 | script-security
90 | 1.39
91 | test
92 |
93 |
94 | org.jenkins-ci.plugins
95 | scm-api
96 | 2.2.6
97 | test
98 |
99 |
100 |
101 |
102 |
103 |
104 | repo.jenkins-ci.org
105 | https://repo.jenkins-ci.org/public/
106 |
107 |
108 | central
109 | https://repo1.maven.org/maven2/
110 |
111 |
112 |
113 |
114 | repo.jenkins-ci.org
115 | https://repo.jenkins-ci.org/public/
116 |
117 |
118 | central
119 | https://repo1.maven.org/maven2/
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/auth/AlibabaCredentials.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.auth;
2 |
3 | import com.alibabacloud.credentials.plugin.client.AlibabaClient;
4 | import com.aliyuncs.auth.AlibabaCloudCredentials;
5 | import com.aliyuncs.ecs.model.v20140526.DescribeRegionsResponse;
6 | import com.cloudbees.plugins.credentials.CredentialsDescriptor;
7 | import com.cloudbees.plugins.credentials.CredentialsScope;
8 | import com.cloudbees.plugins.credentials.NameWith;
9 | import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
10 | import edu.umd.cs.findbugs.annotations.CheckForNull;
11 | import hudson.Extension;
12 | import hudson.security.ACL;
13 | import hudson.util.FormValidation;
14 | import hudson.util.Secret;
15 | import jenkins.model.Jenkins;
16 | import lombok.extern.slf4j.Slf4j;
17 | import org.apache.commons.collections.CollectionUtils;
18 | import org.apache.commons.lang.StringUtils;
19 | import org.kohsuke.stapler.DataBoundConstructor;
20 | import org.kohsuke.stapler.QueryParameter;
21 | import org.kohsuke.stapler.interceptor.RequirePOST;
22 |
23 | import static com.cloudbees.plugins.credentials.CredentialsScope.SYSTEM;
24 | import static hudson.security.Permission.CREATE;
25 | import static hudson.security.Permission.UPDATE;
26 |
27 | import java.util.List;
28 | import java.util.UUID;
29 |
30 |
31 | /**
32 | * Created by kunlun.ykl on 2020/8/26.
33 | */
34 | @NameWith(
35 | value = AlibabaCredentials.NameProvider.class,
36 | priority = 1
37 | )
38 | @Slf4j
39 | public class AlibabaCredentials extends BaseStandardCredentials implements AlibabaCloudCredentials {
40 | private static final long serialVersionUID = -323740205079785605L;
41 | /**
42 | * 主账号的AK, 或者RamRole的临时AK
43 | */
44 | protected String accessKey;
45 | /**
46 | * 主账号的SK, 或者RamRole的临时SK
47 | */
48 | protected Secret secretKey;
49 | public static final String DEFAULT_ECS_REGION = "cn-beijing";
50 |
51 | public AlibabaCredentials(@CheckForNull String accessKey, @CheckForNull String secretKey) {
52 | super(CredentialsScope.GLOBAL, UUID.randomUUID().toString(), "test");
53 | this.accessKey = accessKey;
54 | this.secretKey = Secret.fromString(secretKey);
55 | }
56 |
57 | @DataBoundConstructor
58 | public AlibabaCredentials(@CheckForNull CredentialsScope scope, @CheckForNull String id,
59 | @CheckForNull String accessKey, @CheckForNull String secretKey,
60 | @CheckForNull String description) {
61 | super(scope, id, description);
62 | this.accessKey = accessKey;
63 | this.secretKey = Secret.fromString(secretKey);
64 | }
65 |
66 | public AlibabaCredentials(CredentialsScope scope, String id, String description) {
67 | super(scope, id, description);
68 | }
69 |
70 | public String getAccessKey() {
71 | return accessKey;
72 | }
73 |
74 | public Secret getSecretKey() {
75 | return secretKey;
76 | }
77 |
78 | public String getDisplayName() {
79 | return accessKey;
80 | }
81 |
82 | @Override
83 | public CredentialsScope getScope() {
84 | return SYSTEM;
85 | }
86 |
87 | @Override
88 | public String getAccessKeyId() {
89 | return accessKey;
90 | }
91 |
92 | @Override
93 | public String getAccessKeySecret() {
94 | return secretKey.getPlainText();
95 | }
96 |
97 | public void setAccessKey(String accessKey) {
98 | this.accessKey = accessKey;
99 | }
100 |
101 | public void setSecretKey(String secretKey) {
102 | this.secretKey = Secret.fromString(secretKey);
103 | }
104 |
105 | @Extension
106 | public static class DescriptorImpl extends CredentialsDescriptor {
107 |
108 | @Override
109 | public String getDisplayName() {
110 | return "Alibaba Cloud Credentials";
111 | }
112 |
113 | public ACL getACL() {
114 | return Jenkins.get().getACL();
115 | }
116 |
117 | @RequirePOST
118 | public FormValidation doCheckSecretKey(@QueryParameter("accessKey") String accessKey,
119 | @QueryParameter String value) {
120 | if(!this.getACL().hasPermission(CREATE) && !getACL().hasPermission(UPDATE)){
121 | return FormValidation.error("permission is error");
122 | }
123 |
124 | if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(value)) {
125 | return FormValidation.ok();
126 | }
127 | if (StringUtils.isBlank(accessKey)) {
128 | return FormValidation.error("Illegal Access Key");
129 | }
130 | if (StringUtils.isBlank(value)) {
131 | return FormValidation.error("Illegal Secret Key");
132 | }
133 |
134 | AlibabaCloudCredentials credentials = new AlibabaCredentials(accessKey, value);
135 | AlibabaClient client = new AlibabaClient(credentials, DEFAULT_ECS_REGION, false);
136 | List regions = client.describeRegions();
137 | if (CollectionUtils.isEmpty(regions)) {
138 | return FormValidation.error("Illegal ak/sk");
139 | }
140 | return FormValidation.ok();
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/util/CredentialsHelper.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.util;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibabacloud.credentials.plugin.auth.AlibabaCredentials;
5 | import com.alibabacloud.credentials.plugin.auth.AlibabaSessionTokenCredentials;
6 | import com.aliyuncs.auth.AlibabaCloudCredentials;
7 | import com.cloudbees.plugins.credentials.CredentialsMatchers;
8 | import com.cloudbees.plugins.credentials.CredentialsProvider;
9 | import com.cloudbees.plugins.credentials.SystemCredentialsProvider.StoreImpl;
10 | import com.cloudbees.plugins.credentials.domains.Domain;
11 | import hudson.security.ACL;
12 | import jenkins.model.Jenkins;
13 | import lombok.extern.slf4j.Slf4j;
14 | import org.apache.commons.lang.StringUtils;
15 | import org.apache.commons.lang.exception.ExceptionUtils;
16 |
17 | import javax.annotation.CheckForNull;
18 | import java.io.IOException;
19 | import java.util.Collections;
20 |
21 | /**
22 | * Created by kunlun.ykl on 2020/8/26.
23 | */
24 | @Slf4j
25 | public class CredentialsHelper {
26 | private static final Long REFRESH_THRESHOLD_TIME = 300L;
27 |
28 | @CheckForNull
29 | public static AlibabaCredentials getCredentials(@CheckForNull String credentialsId) {
30 | if (StringUtils.isBlank(credentialsId)) {
31 | log.warn("getCredentials credentialsId is null, credentialsId:{}", credentialsId);
32 | return null;
33 | }
34 | AlibabaCredentials alibabaCredentials = CredentialsMatchers.firstOrNull(
35 | CredentialsProvider.lookupCredentials(AlibabaCredentials.class, Jenkins.get(),
36 | ACL.SYSTEM, Collections.emptyList()),
37 | CredentialsMatchers.withId(credentialsId));
38 | if (alibabaCredentials == null) {
39 | log.warn("getCredentials alibabaCredentials is null, credentialsId:{}", credentialsId);
40 | return null;
41 | }
42 | // 判断是否为token模式
43 | if (isSessionTokenCredentials(alibabaCredentials)) {
44 | if (isRamTokenExpired((AlibabaSessionTokenCredentials) alibabaCredentials)) {
45 | return alibabaCredentials;
46 | }
47 |
48 | // 刷新token
49 | if (refreshRamCredentials((AlibabaSessionTokenCredentials) alibabaCredentials)) {
50 | log.info("refresh success. credentialsId: {}", credentialsId);
51 | } else {
52 | log.warn("refresh failed. credentialsId: {}", credentialsId);
53 | }
54 | }
55 | return alibabaCredentials;
56 | }
57 |
58 | public static boolean isSessionTokenCredentials(AlibabaCloudCredentials alibabaCredentials) {
59 | return alibabaCredentials instanceof AlibabaSessionTokenCredentials;
60 | }
61 |
62 | public static boolean isRamTokenExpired(AlibabaSessionTokenCredentials sessionTokenCredentials) {
63 | Long ramRefreshTime = sessionTokenCredentials.getRamRefreshTime();
64 | Long currentTime = sessionTokenCredentials.getCurrentTime();
65 | Long stsTokenDuration = sessionTokenCredentials.getStsTokenDuration();
66 | long l = currentTime - ramRefreshTime;
67 | long ll = stsTokenDuration - REFRESH_THRESHOLD_TIME;
68 | // ((当前时间 - 上次刷新时间)<= (token超时时间 - 设置阈值时间300)) = 未超时。反之则认为超时
69 | if (l <= ll) {
70 | log.warn("isRamTokenExpired error, regions isEmpty credentialsId:{}", sessionTokenCredentials.getId());
71 | return true;
72 | }
73 | return false;
74 | }
75 |
76 | public static boolean refreshRamCredentials(AlibabaSessionTokenCredentials sessionTokenCredentials) {
77 | AlibabaCredentials parent = sessionTokenCredentials.getParent();
78 | if (parent == null) {
79 | log.error("refreshRamCredentials error, getParent not found, sessionTokenCredentials:{}", JSON.toJSON(sessionTokenCredentials));
80 | return false;
81 | }
82 | try {
83 | // 生成新的token
84 | AlibabaSessionTokenCredentials replacement = new AlibabaSessionTokenCredentials(sessionTokenCredentials.getScope(),
85 | sessionTokenCredentials.getId(),
86 | parent.getAccessKeyId(),
87 | parent.getAccessKeySecret(),
88 | sessionTokenCredentials.getDescription(),
89 | sessionTokenCredentials.getIamRoleArn(),
90 | sessionTokenCredentials.getRoleSessionName(),
91 | sessionTokenCredentials.getStsTokenDuration());
92 | // 修改credentials
93 | updateCredentials(sessionTokenCredentials, replacement);
94 | sessionTokenCredentials.setAccessKey(replacement.getAccessKeyId());
95 | sessionTokenCredentials.setSecretKey(replacement.getAccessKeySecret());
96 | sessionTokenCredentials.setSecretToken(replacement.getSecretToken());
97 | return true;
98 | } catch (IOException e) {
99 | log.error("refreshRamCredentials error, credentialsId:{}, e:{}", sessionTokenCredentials.getId(), ExceptionUtils.getStackTrace(e));
100 | }
101 | return false;
102 | }
103 |
104 | private static void updateCredentials(AlibabaSessionTokenCredentials alibabaCredentials, AlibabaSessionTokenCredentials replacement) throws IOException {
105 | // get credentials domain
106 | Domain domain = Domain.global();
107 | // get credentials store
108 | StoreImpl store = new StoreImpl();
109 | // add credential to store
110 | store.updateCredentials(domain, alibabaCredentials, replacement);
111 | }
112 | }
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/auth/AlibabaKeyPairUtils.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.auth;
2 |
3 |
4 | import com.alibabacloud.credentials.plugin.client.AlibabaClient;
5 | import com.aliyuncs.ecs.model.v20140526.DescribeKeyPairsResponse.KeyPair;
6 | import jenkins.bouncycastle.api.PEMEncodable;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.apache.commons.codec.binary.Base64;
9 | import org.apache.commons.codec.binary.Hex;
10 | import org.apache.commons.collections.CollectionUtils;
11 | import org.apache.commons.lang.StringUtils;
12 |
13 | import java.io.BufferedReader;
14 | import java.io.IOException;
15 | import java.io.StringReader;
16 | import java.io.UnsupportedEncodingException;
17 | import java.math.BigInteger;
18 | import java.security.MessageDigest;
19 | import java.security.NoSuchAlgorithmException;
20 | import java.security.UnrecoverableKeyException;
21 | import java.security.interfaces.RSAPublicKey;
22 | import java.util.List;
23 |
24 | /**
25 | * Created by kunlun.ykl on 2020/9/21.
26 | */
27 | @Slf4j
28 | public class AlibabaKeyPairUtils {
29 |
30 | public static String getPublicFingerprint(String pemData) {
31 | if (pemData == null || pemData.isEmpty()) {
32 | log.error("This private key cannot be empty");
33 | return null;
34 | }
35 |
36 | java.security.KeyPair kp = null;
37 | try {
38 | kp = PEMEncodable.decode(pemData).toKeyPair();
39 | } catch (IOException e) {
40 | log.error("This private key is password protected, which isn't supported yet");
41 | } catch (UnrecoverableKeyException e) {
42 | log.error("This private key is password protected, which isn't supported yet");
43 | }
44 | if(kp == null ){
45 | return null;
46 | }
47 | RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
48 | byte[] keyType = new byte[0];
49 | try {
50 | keyType = "ssh-rsa".getBytes("UTF-8");
51 | } catch (UnsupportedEncodingException e) {
52 | log.error("This private key is password protected, which isn't supported yet");
53 | }
54 | byte[] expBlob = publicKey.getPublicExponent().toByteArray();
55 | byte[] modBlob = publicKey.getModulus().toByteArray();
56 | byte[] authKeyBlob = new byte[3 * 4 + keyType.length + expBlob.length + modBlob.length];
57 |
58 | byte[] lenArray = BigInteger.valueOf(keyType.length).toByteArray();
59 | System.arraycopy(lenArray, 0, authKeyBlob, 4 - lenArray.length, lenArray.length);
60 | System.arraycopy(keyType, 0, authKeyBlob, 4, keyType.length);
61 |
62 | lenArray = BigInteger.valueOf(expBlob.length).toByteArray();
63 | System.arraycopy(lenArray, 0, authKeyBlob, 4 + keyType.length + 4 - lenArray.length, lenArray.length);
64 | System.arraycopy(expBlob, 0, authKeyBlob, 4 + (4 + keyType.length), expBlob.length);
65 |
66 | lenArray = BigInteger.valueOf(modBlob.length).toByteArray();
67 | System.arraycopy(lenArray, 0, authKeyBlob, 4 + expBlob.length + 4 + keyType.length + 4 - lenArray.length,
68 | lenArray.length);
69 | System.arraycopy(modBlob, 0, authKeyBlob, 4 + (4 + expBlob.length + (4 + keyType.length)), modBlob.length);
70 | String pubKeyBody = null;
71 | try {
72 | pubKeyBody = new String(Base64.encodeBase64(authKeyBlob),"UTF-8");
73 | } catch (UnsupportedEncodingException e) {
74 | log.error("This private key is password protected, which isn't supported yet");
75 | }
76 | byte[] bytes = Base64.decodeBase64(pubKeyBody);
77 | MessageDigest messageDigest = null;
78 | String pubKeyFingerPrint="";
79 | try {
80 | messageDigest = MessageDigest.getInstance("MD5");
81 | messageDigest.update(bytes);
82 | byte[] resultByteArray = messageDigest.digest();
83 | pubKeyFingerPrint = Hex.encodeHexString(resultByteArray);
84 | } catch (NoSuchAlgorithmException e) {
85 | log.error("This private key is password protected, which isn't supported yet");
86 | }
87 |
88 | return pubKeyFingerPrint;
89 | }
90 |
91 | /**
92 | * Finds the {@link KeyPair} that corresponds to this key in ECS.
93 | */
94 | public static KeyPair find(String pemData, AlibabaCredentials credentials, String regionNo) {
95 | String pfp = getPublicFingerprint(pemData);
96 | if (StringUtils.isBlank(pfp)) {
97 | log.error("getPublicFingerprint error");
98 | return null;
99 | }
100 | AlibabaClient client = new AlibabaClient(credentials, regionNo, false);
101 | List keyPairs = client.describeKeyPairs(null, pfp);
102 | if (CollectionUtils.isEmpty(keyPairs)) {
103 | log.error("find keyPairs error. regionNo: {} pfp: {}", regionNo, pfp);
104 | return null;
105 | }
106 | return keyPairs.get(0);
107 | }
108 |
109 | public static KeyPair find(String pemData, AlibabaCredentials credentials, String regionNo, boolean isVpcEnv) {
110 | String pfp = getPublicFingerprint(pemData);
111 | if (StringUtils.isBlank(pfp)) {
112 | log.error("getPublicFingerprint error");
113 | return null;
114 | }
115 | AlibabaClient client = new AlibabaClient(credentials, regionNo, isVpcEnv);
116 | List keyPairs = client.describeKeyPairs(null, pfp);
117 | if (CollectionUtils.isEmpty(keyPairs)) {
118 | log.error("find keyPairs error. regionNo: {} pfp: {}", regionNo, pfp);
119 | return null;
120 | }
121 | return keyPairs.get(0);
122 | }
123 |
124 |
125 | /**
126 | * Is this file really a private key?
127 | */
128 | public static boolean isPrivateKey(String pemData) throws IOException {
129 | BufferedReader br = new BufferedReader(new StringReader(pemData));
130 | String line;
131 | while ((line = br.readLine()) != null) {
132 | if (line.equals("-----BEGIN RSA PRIVATE KEY-----")) { return true; }
133 | }
134 | return false;
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/alibabacloud/credentials/plugin/auth/AlibabaSessionTokenCredentials.java:
--------------------------------------------------------------------------------
1 | package com.alibabacloud.credentials.plugin.auth;
2 |
3 | import com.alibabacloud.credentials.plugin.client.AlibabaClient;
4 | import com.aliyuncs.auth.sts.AssumeRoleResponse;
5 | import com.aliyuncs.ecs.model.v20140526.DescribeRegionsResponse;
6 | import com.aliyuncs.exceptions.ClientException;
7 | import com.cloudbees.plugins.credentials.CredentialsDescriptor;
8 | import com.cloudbees.plugins.credentials.CredentialsScope;
9 | import edu.umd.cs.findbugs.annotations.CheckForNull;
10 | import hudson.Extension;
11 | import hudson.security.ACL;
12 | import hudson.util.FormValidation;
13 | import hudson.util.Secret;
14 | import jenkins.model.Jenkins;
15 | import lombok.extern.slf4j.Slf4j;
16 | import org.apache.commons.collections.CollectionUtils;
17 | import org.apache.commons.lang.StringUtils;
18 | import org.apache.commons.lang.exception.ExceptionUtils;
19 | import org.kohsuke.stapler.DataBoundConstructor;
20 | import org.kohsuke.stapler.QueryParameter;
21 | import org.kohsuke.stapler.interceptor.RequirePOST;
22 |
23 | import java.util.Calendar;
24 | import java.util.Date;
25 | import java.util.List;
26 | import java.util.UUID;
27 |
28 | import static hudson.security.Permission.CREATE;
29 | import static hudson.security.Permission.UPDATE;
30 |
31 | /**
32 | * @author gaojiahao wb511401
33 | * @description
34 | * @date 2022/8/16 下午5:56
35 | */
36 | @Slf4j
37 | public class AlibabaSessionTokenCredentials extends AlibabaCredentials implements AlibabaCloudRamCredentials {
38 |
39 | private static final long serialVersionUID = -4185406790852698614L;
40 |
41 | private String secretToken;
42 |
43 | private String iamRoleArn;
44 |
45 | private String roleSessionName;
46 |
47 | // token持续时间
48 | private Long stsTokenDuration;
49 |
50 | // token上次刷新时间
51 | private Long ramRefreshTime;
52 |
53 | public static final Long STS_CREDENTIALS_DURATION_SECONDS = 3600L;
54 |
55 | private AlibabaCredentials parent;
56 |
57 | public static final String DEFAULT_ECS_REGION = "cn-beijing";
58 |
59 | public AlibabaSessionTokenCredentials(String accessKey, String secretKey, String securityToken) {
60 | super(CredentialsScope.GLOBAL, UUID.randomUUID().toString(), "test");
61 | this.accessKey = accessKey;
62 | this.secretKey = Secret.fromString(secretKey);
63 | this.secretToken = securityToken;
64 | }
65 |
66 | @DataBoundConstructor
67 | public AlibabaSessionTokenCredentials(@CheckForNull CredentialsScope scope, @CheckForNull String id,
68 | @CheckForNull String parentAccessKey,@CheckForNull String parentSecretKey,
69 | @CheckForNull String description, @CheckForNull String iamRoleArn,
70 | @CheckForNull String roleSessionName, @CheckForNull Long stsTokenDuration) {
71 | super(scope, id, description);
72 | this.parent = new AlibabaCredentials(parentAccessKey, parentSecretKey);
73 | this.iamRoleArn = iamRoleArn;
74 | this.roleSessionName = roleSessionName;
75 | this.stsTokenDuration = stsTokenDuration;
76 | readResolve();
77 | }
78 |
79 |
80 |
81 | protected Object readResolve() {
82 | AlibabaClient alibabaClient = new AlibabaClient(parent, DEFAULT_ECS_REGION, false);
83 | try {
84 | AssumeRoleResponse assumeRoleRequest = alibabaClient.createAssumeRoleRequest(iamRoleArn, roleSessionName, stsTokenDuration);
85 | this.accessKey = assumeRoleRequest.getCredentials().getAccessKeyId();
86 | this.secretKey = Secret.fromString(assumeRoleRequest.getCredentials().getAccessKeySecret());
87 | this.secretToken = assumeRoleRequest.getCredentials().getSecurityToken();
88 | this.ramRefreshTime = getCurrentTime();
89 | } catch (ClientException e) {
90 | log.error("createAssumeRoleRequest error, e:{}", ExceptionUtils.getStackTrace(e));
91 | }
92 | return this;
93 | }
94 |
95 | public String getParentAccessKey(){
96 | return parent.getAccessKeyId();
97 | }
98 |
99 | public String getParentSecretKey(){
100 | return parent.getAccessKeySecret();
101 | }
102 |
103 | /**
104 | * 获取当前时间,单位秒
105 | * @return Long
106 | */
107 | public Long getCurrentTime (){
108 | Date date = new Date();
109 | return date.getTime() / 1000;
110 | }
111 |
112 | public Long getRamRefreshTime() {
113 | return ramRefreshTime;
114 | }
115 |
116 | public void setSecretToken(String secretToken) {
117 | this.secretToken = secretToken;
118 | }
119 |
120 | public String getIamRoleArn() {
121 | return iamRoleArn;
122 | }
123 |
124 | public String getRoleSessionName() {
125 | return roleSessionName;
126 | }
127 |
128 | public Long getStsTokenDuration() {
129 | return stsTokenDuration == null ? DescriptorImpl.DEFAULT_STS_TOKEN_DURATION : stsTokenDuration;
130 | }
131 |
132 | public AlibabaCredentials getParent() {
133 | return parent;
134 | }
135 |
136 |
137 | public String getSecretToken() {
138 | return secretToken;
139 | }
140 |
141 | @Extension
142 | public static class DescriptorImpl extends CredentialsDescriptor {
143 |
144 | @Override
145 | public String getDisplayName() {
146 | return "Alibaba SessionToken Cloud Credentials";
147 | }
148 |
149 | public ACL getACL() {
150 | return Jenkins.get().getACL();
151 | }
152 |
153 | public static final Long DEFAULT_STS_TOKEN_DURATION = STS_CREDENTIALS_DURATION_SECONDS;
154 |
155 | @RequirePOST
156 | public FormValidation doCheckParentSecretKey(@QueryParameter("parentAccessKey") String accessKey,
157 | @QueryParameter("iamRoleArn") String iamRoleArn,
158 | @QueryParameter("roleSessionName") String roleSessionName,
159 | @QueryParameter("stsTokenDuration") Long stsTokenDuration,
160 | @QueryParameter String value) {
161 | if (!this.getACL().hasPermission(CREATE) && !getACL().hasPermission(UPDATE)) {
162 | return FormValidation.error("permission is error");
163 | }
164 |
165 | if (StringUtils.isBlank(accessKey) && StringUtils.isBlank(value)) {
166 | return FormValidation.ok();
167 | }
168 | if (StringUtils.isBlank(accessKey)) {
169 | return FormValidation.error("Illegal Access Key");
170 | }
171 | if (StringUtils.isBlank(value)) {
172 | return FormValidation.error("Illegal Secret Key");
173 | }
174 |
175 | AlibabaCredentials credentials = new AlibabaCredentials(accessKey, value);
176 | AlibabaClient client = new AlibabaClient(credentials, DEFAULT_ECS_REGION, false);
177 | // If iamRoleArn is specified, swap out the credentials.
178 | if (!StringUtils.isBlank(iamRoleArn)) {
179 | try {
180 | AssumeRoleResponse acsResponse = client.createAssumeRoleRequest(iamRoleArn, roleSessionName, stsTokenDuration);
181 | AlibabaCloudRamCredentials alibabaSessionTokenCredentials = new AlibabaSessionTokenCredentials(
182 | acsResponse.getCredentials().getAccessKeyId(),
183 | acsResponse.getCredentials().getAccessKeySecret(),
184 | acsResponse.getCredentials().getSecurityToken());
185 | client = new AlibabaClient(alibabaSessionTokenCredentials, DEFAULT_ECS_REGION, false);
186 | } catch (Exception e) {
187 | log.error("Unable to assume role [" + iamRoleArn + "] with request。" + e);
188 | return FormValidation.error("Unable to assume role [" + iamRoleArn + "] with request。" + e);
189 | }
190 | }
191 | List regions = client.describeRegions();
192 | if (CollectionUtils.isEmpty(regions)) {
193 | return FormValidation.error("Illegal ak/sk");
194 | }
195 | return FormValidation.ok();
196 | }
197 |
198 | }
199 | }
--------------------------------------------------------------------------------