├── 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 | ![](docs/images/alibaba.ak.png) 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 | ![](docs/images/alibaba.getak.png) 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 | ![](docs/images/jenkins.managePlugin.png) 30 | 31 | * On the plug-in management page, enter "Alibaba cloud credentials" in the query box to query 32 | the plugin 33 | ![](docs/images/jenkins.available.png) 34 | 35 | * Scroll to the bottom and select "install without restart" 36 | ![](docs/images/jenkins.install.png) 37 | 38 | ### Verify 39 | Once you've installed the plugin, you can verify AK, SK or save it in "manage credentials" 40 | ![](docs/images/jenkins.credential.png) 41 | ![](docs/images/jenkins.verify.png) 42 | 43 | # Contact us 44 | * DingTalk Group Number:44723358 45 | * DingTalk Group QR code 46 | 47 | ![](docs/images/qrcode.png) -------------------------------------------------------------------------------- /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 | } --------------------------------------------------------------------------------