こんにちは。森です。
以前、Route53を使用してLet’s Encryptで発行されるSSL証明書の取得を行ったので備忘録的に記載をしていきます。
通常、Let’s Encryptで発行されたSSL証明書を取得する場合や更新には80番 or 443番ポートをアクセス可能にしておく必要があると思いますが、何かしらの事情でそれらのポートを開けることが出来ない方には参考になる記事かと思います。
今回は下記環境で行ってみました。
- 環境
AmazonLinux2
詳細な手順な手順については下記を見てみてください。
詳細な手順
手順1. 使用できるPythonのバージョン確認
今回は、python3.8がインストールできることが下記でわかる。
1 2 3 |
[root@xxx-web-ec2] # amazon-linux-extras list | grep python 44 python3.8=latest enabled [ =stable ] |
手順2. Pythonインストールと確認
インストール
1 2 |
[root@xxx-web-ec2] # amazon-linux-extras install -y python3.8 |
バージョンの確認
1 2 3 |
[root@xxx-web-ec2] # python3.8 -V Python 3.8.16 |
手順3. aliasの設定と確認
aliasの設定
1 2 3 4 |
[root@xxx-web-ec2] # echo 'alias python=python3.8' >> ~/.bashrc [root@xxx-web-ec2] # echo 'alias pip=pip3.8' >> ~/.bashrc [root@xxx-web-ec2] # source ~/.bashrc |
.bashrcの確認
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@xxx-web-ec2] # cat ~/.bashrc # .bashrc # User specific aliases and functions alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi alias python=python3.8 alias pip=pip3.8 |
バージョンの確認
1 2 3 4 5 6 |
[root@xxx-web-ec2] # python -V Python 3.8.16 [root@xxx-web-ec2] # pip -V pip 21.0.1 from /usr/lib/python3.8/site-packages/pip (python 3.8) |
手順4. pipenvのインストール
1 2 |
[root@xxx-web-ec2] # pip install pipenv |
手順5. Certbotのインストール
1 2 |
[root@xxx-web-ec2] # pip install certbot |
手順6. certbot-dns-route53のインストール
1 2 |
[root@xxx-web-ec2] # pip install certbot-dns-route53 |
手順7. pipのインストール済みのパッケージ一覧を確認
下記のように certbot と certbot-dns-route53 が入っていれば問題ない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
[root@xxx-web-ec2] # pip list Package Version ------------------- --------- acme 2.2.0 boto3 1.26.69 botocore 1.29.69 certbot 2.2.0 ← これ certbot-dns-route53 2.2.0 ← これ certifi 2022.12.7 cffi 1.15.1 charset-normalizer 3.0.1 ConfigArgParse 1.5.3 configobj 5.0.8 cryptography 39.0.1 distlib 0.3.6 distro 1.8.0 filelock 3.9.0 idna 3.4 jmespath 1.0.1 josepy 1.13.0 parsedatetime 2.6 pip 21.0.1 pipenv 2023.2.4 platformdirs 3.0.0 pycparser 2.21 pyOpenSSL 23.0.0 pyRFC3339 1.1 python-dateutil 2.8.2 pytz 2022.7.1 requests 2.28.2 s3transfer 0.6.0 setuptools 67.2.0 six 1.16.0 urllib3 1.26.14 virtualenv 20.19.0 virtualenv-clone 0.5.7 |
手順8. IAMロールとIAMポリシーの作成
IAMロールの作成
適当に作成して構いません。
IAMポリシーの作成
ポリシーは下記のように作成して頂ければ問題ないかと思います。
xxxxxxIDという部分のみ自分が更新するドメインのホストゾーンIDを記載してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{ "Version": "2012-10-17", "Id": "certbot-dns-route53 sample policy", "Statement": [ { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:GetChange" ], "Resource": [ "*" ] }, { "Effect" : "Allow", "Action" : [ "route53:ChangeResourceRecordSets" ], "Resource" : [ "arn:aws:route53:::hostedzone/xxxxxxID" ] } ] } |
手順9. SSL証明書の発行
今回は test-ssl.net というSSL証明書を発行する手順で記載しております。
1 2 3 4 5 6 |
certbot --debug certonly \ --email 'xxx.xxx@xxx.xxx' \ --agree-tos \ --dns-route53 \ -d test-ssl.net |
上手くいけば下記のような結果が出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: Y Account registered. Requesting a certificate for test-ssl.net Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/test-ssl.net /fullchain.pem Key is saved at: /etc/letsencrypt/live/test-ssl.net /privkey.pem This certificate expires on 2023-05-14. These files will be updated when the certificate renews. NEXT STEPS: - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions. We were unable to subscribe you the EFF mailing list because your e-mail address appears to be invalid. You can try again later by visiting https://act.eff.org. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If you like Certbot, please consider supporting our work by: * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate * Donating to EFF: https://eff.org/donate-le - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
手順10. SSL証明書の確認
発行された場所に行くと問題なく下記のような証明書があれば成功です。
1 2 3 4 5 6 7 8 |
drwxr-xr-x 2 root root 93 2月 13 14:27 . drwx------ 3 root root 54 2月 13 14:27 .. -rw-r--r-- 1 root root 692 2月 13 14:27 README lrwxrwxrwx 1 root root 50 2月 13 14:27 cert.pem -> ../../archive/test-ssl.net /cert1.pem lrwxrwxrwx 1 root root 51 2月 13 14:27 chain.pem -> ../../archive/test-ssl.net /chain1.pem lrwxrwxrwx 1 root root 55 2月 13 14:27 fullchain.pem -> ../../archive/test-ssl.net /fullchain1.pem lrwxrwxrwx 1 root root 53 2月 13 14:27 privkey.pem -> ../../archive/test-ssl.net /privkey1.pem |
あとはCronで定期的に下記コマンドで実行してあげれば自動更新が出来ると思います。
1 2 |
certbot renew |
有効期限が来ていない状態で更新したい場合
手順1から手順10まで行えばSSL証明書が発行されると思います。
もし発行されてからちゃんと更新されるか心配という方は下記コマンドを使用すれば有効期限が来ていなくても無理やり更新が出来ますので試してみてください。
1 2 |
certbot renew --force-renewal |
補足
手順1から手順8まで作成するCloudFormationのテンプレートを下記に記載しておきますのでよかったら使用してみてください。※手順7は除く。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
AWSTemplateFormatVersion: "2010-09-09" Description: VPC + EC2 Create Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "Project Name Prefix" Parameters: - PJPrefix - Label: default: "Network Configuration" Parameters: - VPCCIDR - PublicSubnetACIDR - Label: default: "EC2Instance Configuration" Parameters: - KeyPairName - EC2InstanceName - EC2InstanceAMI - EC2InstanceInstanceType - EC2InstanceVolumeType - EC2InstanceVolumeSize - AllowIP ParameterLabels: # Network Configuration VPCCIDR: default: "VPC CIDR" PublicSubnetACIDR: default: "PublicSubnetA CIDR" # EC2Instance Configuration KeyPairName: default: "KeyPairName" EC2InstanceName: default: "EC2 Name" EC2InstanceAMI: default: "EC2 AMI" EC2InstanceInstanceType: default: "EC2 InstanceType" EC2InstanceVolumeType: default: "EC2 VolumeType" EC2InstanceVolumeSize: default: "EC2 VolumeSize" # Securitygroup Configuration AllowIP: default: "AllowIP" # hostedzoneID Configuration HostedzoneID: default: "HostedzoneID" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: PJPrefix: Type: String # Network VPCCIDR: Type: String Default: "10.1.0.0/16" PublicSubnetACIDR: Type: String Default: "10.1.1.0/24" # EC2Instance KeyPairName: Type: AWS::EC2::KeyPair::KeyName EC2InstanceName: Type: String EC2InstanceAMI: Type: String Default: "ami-xxxxx" EC2InstanceInstanceType: Type: String Default: "t3.nano" EC2InstanceVolumeType: Type: String Default: "gp2" EC2InstanceVolumeSize: Type: String Default: "8" # Securitygroup AllowIP: Type: String Default: xx.xx.xx.xx/32 # hostedzoneID Configuration HostedzoneID: Type: String Default: "xxxxxxxx" Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# # VPC Create VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: !Ref VPCCIDR InstanceTenancy: default EnableDnsSupport: "true" EnableDnsHostnames: "true" Tags: - Key: Name Value: !Sub "${PJPrefix}-vpc" # InternetGateway Create InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Sub "${PJPrefix}-igw" # IGW Attach InternetGatewayAttachment: Type: "AWS::EC2::VPCGatewayAttachment" Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ------------------------------------------------------------# # EC2 Subnet # ------------------------------------------------------------ PublicSubnetA: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-1a" CidrBlock: !Ref PublicSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${PJPrefix}-public02-1a" # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PublicRouteTable: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${PJPrefix}-public-route01" # ------------------------------------------------------------# # Route # ------------------------------------------------------------# PublicRouteMain: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway # ------------------------------------------------------------# # EC2 RouteTable Associate # ------------------------------------------------------------# PublicSubnetARouteTableAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref PublicRouteTable # ------------------------------------------------------------# # IAM Policy(ManagedPolicy) # ------------------------------------------------------------# WEBEC2IAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: !Sub "${PJPrefix}-${EC2InstanceName}-policy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "route53:GetChange" - "route53:ListHostedZones" Resource: "*" - Effect: Allow Action: - "route53:ChangeResourceRecordSets" Resource: - !Sub "arn:aws:route53:::hostedzone/${HostedzoneID}" Roles: - !Ref WEBEC2IAMRole # ------------------------------------------------------------# # IAM Role for WEB EC2 # ------------------------------------------------------------# WEBEC2IAMRole: Type: "AWS::IAM::Role" Properties: RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - "ec2.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" ManagedPolicyArns: - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" WEBEC2InstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: "/" Roles: - Ref: WEBEC2IAMRole InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile" # ------------------------------------------------------------# # WEB EC2Instance AZ:A # ------------------------------------------------------------# WEBEC2Instance01: Type: "AWS::EC2::Instance" Properties: Tags: - Key: Name Value: !Sub "${PJPrefix}-${EC2InstanceName}02" ImageId: !Ref EC2InstanceAMI InstanceType: !Ref EC2InstanceInstanceType KeyName: !Ref KeyPairName IamInstanceProfile: !Ref WEBEC2InstanceProfile DisableApiTermination: false EbsOptimized: false BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true VolumeType: !Ref EC2InstanceVolumeType VolumeSize: !Ref EC2InstanceVolumeSize SecurityGroupIds: - !Ref PublicSecurityGroup SubnetId: !Ref PublicSubnetA UserData: !Base64 | #! /bin/bash timedatectl set-timezone Asia/Tokyo hostnamectl set-hostname xxx-web-ec2 localectl set-locale LANG=ja_JP.utf8 yum update amazon-linux-extras install -y python3.8 echo 'alias python=python3.8' >> ~/.bashrc echo 'alias pip=pip3.8' >> ~/.bashrc source ~/.bashrc pip3.8 install pipenv pip3.8 install certbot-dns-route53 # ------------------------------------------------------------# # WEB SecurityGroup for EC2 public # ------------------------------------------------------------# # SecurityGroup PublicSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: VpcId: !Ref VPC GroupName: !Sub "${PJPrefix}-public-sg01" GroupDescription: "-" Tags: - Key: "Name" Value: !Sub "${PJPrefix}-public-sg01" # Rule01 SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref AllowIP # ------------------------------------------------------------# # ElasticIP for EC2Instance01 # ------------------------------------------------------------# ElasticIP01: Type: "AWS::EC2::EIP" Properties: Domain: vpc ElasticIPAssociate01: Type: AWS::EC2::EIPAssociation Properties: AllocationId: !GetAtt ElasticIP01.AllocationId InstanceId: !Ref WEBEC2Instance01 |
最後に
いかがだったでしょうか。
なかなか使用する機会はないかもしれませんが、Route53を使用してLet’s Encryptで発行されるSSL証明書の取得することが出来るという事が分かりましたね。
この記事が誰かの助けになれば幸いです。
それでは。