dflook / cloudformation-dns-certificate

Cloudformation DNS Validated Certificate Resource

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SubjectAlternativeNames not working properly

RekhaPGE opened this issue · comments

Hi,

I am trying to add SubjectAlternativeNames to add additional domain (e.g..additional.example.com)
I am following this syntax:
Properties:
SubjectAlternativeNames:
- additional.example.com

But CFT throwing the below error:
Received response status [FAILED] from custom resource. Message returned: DomainValidationOptions missing for additional.example.com (RequestId: 9758388c-7dba-4c2a-a15b-bfa8f06a0931)

Can you please let me know why Lambda is sending FAILED status to CFT and kindly provide the update.

Regards,
Rekha

Hello @RekhaRavula, you need to provide a DomainValidationOptions for your domain. See the example here. Can you post the full resource from the template?

@dflook,

Please find the below resource from our template, we are trying to use secondary domain name xyz.nonprod.pqr.com (e.x) using

generatedCertificate:
    Properties:
      DomainName: !Ref pWebsiteFQDN
      DomainValidationOptions:
        - DomainName: !Ref pWebsiteFQDN
          HostedZoneId: !Ref pHostedZoneId
          Route53RoleArn: !Ref pRoute53AssumedRoleArn
          SubjectAlternativeNames: 
            - xyz.nonprod.pqr.com
      Tags:
        - Key: AppID
          Value: !Ref pAppID
        - Key: Environment
          Value: !Ref pEnv
        - Key: Notify
          Value: !Ref pNotify
        - Key: Order
          Value: !Ref pOrderNumber
        - Key: Org
          Value: !Ref pOrg
        - Key: AppName
          Value: !Ref pAppName
        - Key: Owner
          Value: !Ref pCFNOwnerTag
        - Key: Compliance
          Value: None
        - Key: DataClassification
          Value: Confidential
        - Key: CRIS
          Value: N/A
        - Key: MCP
          Value: "noMcp"
      ValidationMethod: DNS
      Region: us-east-1
      ServiceToken: !GetAtt 'CustomAcmCertificateLambda.Arn'
    Type: Custom::DNSCertificate

But Lambda throwing the below error:

[ERROR] 2021-07-09T02:10:27.28Z 40256572-6e9e-4696-9d58-a7ad516354df 
Traceback (most recent call last): 
File "/var/task/index.py", line 93, in handler 
	if K not in e:g() 
File "/var/task/index.py", line 23, in g 
	for H in set([A[M]]+A.get('SubjectAlternativeNames',[])):k(H) 
File "/var/task/index.py", line 70, in k 
	raise U(N+' missing for '+n) 
RuntimeError: DomainValidationOptions missing for xyz.nonprod.pqr.com

Note: What I'm suspecting is Lambda is unable to read DomainValidationOptions for secondary domain which we are trying to add through SubjectAlternativeNames

SubjectAlternativeNames names should not be inside DomainValidationOptions - is that a copy paste error?

You will need a DomainValidationOptions zone for all zones needed - both the DomainName zone and the zone for any SubjectAlternativeNames (if different).

So it would look something like:

generatedCertificate:
    Properties:
      DomainName: !Ref pWebsiteFQDN
      SubjectAlternativeNames: 
        - xyz.nonprod.pqr.com
      DomainValidationOptions:
        - DomainName: !Ref pWebsiteFQDN
          HostedZoneId: !Ref pHostedZoneId
          Route53RoleArn: !Ref pRoute53AssumedRoleArn
        - DomainName: nonprod.pqr.com
          HostedZoneId: !Ref NonprodZoneId
          Route53RoleArn: !Ref pRoute53AssumedRoleArn
      ValidationMethod: DNS
      Region: us-east-1
      ServiceToken: !GetAtt 'CustomAcmCertificateLambda.Arn'
    Type: Custom::DNSCertificate

As per your suggestion, I used the same code you mentioned above but still getting the same error, can you please confirm in your test environment were you able to add the secondary domain (SubjectAlternativeNames) to your certificate. I think Lambda code is not able to validate the secondary domain and going to exception in the below code:

def k(n):
	C='.';n=n.rstrip(C);D={A[p].rstrip(C):A for A in B[N]};A=n.split(C)
	while len(A):
		if C.join(A)in D:return D[C.join(A)]
		A=A[1:]
	raise T(N+' missing'+' for '+n)

The Exception is telling you something. Please examine and check your resource is correct.

Two domains are in the same AWS account, one is getting updated correctly but secondary domain is failing at validation level. Our resource is correct.

@RekhaRavula Can you post the actual resource you are using

Please find below:

Resources:

  CustomAcmCertificateLambda:
    Metadata:
      Source: https://github.com/dflook/cloudformation-dns-certificate
      Version: 1.7.3
    Properties:
      Code:
        ZipFile: "x=Exception\nV=True\nU=RuntimeError\nimport copy,hashlib as s,json,logging\
          \ as B,time\nfrom boto3 import client as J\nfrom botocore.exceptions import\
          \ ClientError as t,ParamValidationError as u\nfrom urllib.request import\
          \ Request as v,urlopen as w\nA=B.getLogger()\nA.setLevel(B.INFO)\nC=A.info\n\
          R=A.exception\nL=copy.copy\nS=time.sleep\nT=lambda j:json.dumps(j,sort_keys=V).encode()\n\
          K='R'\ndef handler(e,c):\n\tAA='OldResourceProperties';A9='Update';A8='Delete';A7='None';A6='acm';A5='FAILED';A4='properties';A3='stack-id';A2='logical-id';A1='DNS';r='Old';p='Certificate';q='LogicalResourceId';o='ValidationMethod';n='Route53RoleArn';m='Region';d='RequestType';b='StackId';a=None;Q='Status';P='Key';O='';N='DomainValidationOptions';M='DomainName';I='ResourceProperties';H='cloudformation:';G='Value';F='CertificateArn';E='Tags';B='PhysicalResourceId';f=c.get_remaining_time_in_millis;C(e)\n\
          \tdef g():\n\t\tC=L(A)\n\t\tfor G in ['ServiceToken',m,E,n]:C.pop(G,a)\n\
          \t\tif o in A:\n\t\t\tif A[o]==A1:\n\t\t\t\tfor H in set([A[M]]+A.get('SubjectAlternativeNames',[])):k(H)\n\
          \t\t\t\tdel C[N]\n\t\te[B]=D.request_certificate(IdempotencyToken=z,**C)[F];l()\n\
          \tdef W(a):\n\t\twhile V:\n\t\t\ttry:D.delete_certificate(**{F:a});return\n\
          \t\t\texcept t as B:\n\t\t\t\tR(O);A=B.response['Error']['Code']\n\t\t\t\
          \tif A=='ResourceInUseException':\n\t\t\t\t\tif f()/1000<30:raise\n\t\t\t\
          \t\tS(5);continue\n\t\t\t\tif A in['ResourceNotFoundException','ValidationException']:return\n\
          \t\t\t\traise\n\t\t\texcept u:return\n\tdef X(p):\n\t\tfor I in D.get_paginator('list_certificates').paginate():\n\
          \t\t\tfor A in I['CertificateSummaryList']:\n\t\t\t\tC(A)\n\t\t\t\tif p[M].lower()==A[M]:\n\
          \t\t\t\t\tB={B[P]:B[G]for B in D.list_tags_for_certificate(**{F:A[F]})[E]}\n\
          \t\t\t\t\tif B.get(H+A2)==e[q]and B.get(H+A3)==e[b]and B.get(H+A4)==Y(p):return\
          \ A[F]\n\tdef h():\n\t\tif K in e:raise U('Certificate not issued in time')\n\
          \t\te[K]=K;C(e);J('lambda').invoke(FunctionName=c.invoked_function_arn,InvocationType='Event',Payload=T(e))\n\
          \tdef i():\n\t\twhile f()/1000>30:\n\t\t\tA=D.describe_certificate(**{F:e[B]})[p];C(A)\n\
          \t\t\tif A[Q]=='ISSUED':return V\n\t\t\telif A[Q]==A5:raise U(A.get('FailureReason',O))\n\
          \t\t\tS(5)\n\t\treturn False\n\tdef y():A=L(e[r+I]);A.pop(E,a);B=L(e[I]);B.pop(E,a);return\
          \ A!=B\n\tdef j():\n\t\tX='Type';W='Name';U='HostedZoneId';T='ValidationStatus';R='PENDING_VALIDATION';K='ResourceRecord'\n\
          \t\tif A.get(o)!=A1:return\n\t\twhile V:\n\t\t\tH=D.describe_certificate(**{F:e[B]})[p];C(H)\n\
          \t\t\tif H[Q]!=R:return\n\t\t\tif not[A for A in H.get(N,[{}])if T not in\
          \ A or K not in A]:break\n\t\t\tS(1)\n\t\tfor E in H[N]:\n\t\t\tif E[T]==R:L=k(E[M]);O=L.get(n,A.get(n));I=J('sts').assume_role(RoleArn=O,RoleSessionName=(p+e[q])[:64],DurationSeconds=900)['Credentials']if\
          \ O is not a else{};P=J('route53',aws_access_key_id=I.get('AccessKeyId'),aws_secret_access_key=I.get('SecretAccessKey'),aws_session_token=I.get('SessionToken')).change_resource_record_sets(**{U:L[U],'ChangeBatch':{'Comment':'Domain\
          \ validation for '+e[B],'Changes':[{'Action':'UPSERT','ResourceRecordSet':{W:E[K][W],X:E[K][X],'TTL':60,'ResourceRecords':[{G:E[K][G]}]}}]}});C(P)\n\
          \tdef k(n):\n\t\tC='.';n=n.rstrip(C);D={B[M].rstrip(C):B for B in A[N]};B=n.split(C)\n\
          \t\twhile len(B):\n\t\t\tif C.join(B)in D:return D[C.join(B)]\n\t\t\tB=B[1:]\n\
          \t\traise U(N+' missing for '+n)\n\tY=lambda v:s.new('md5',T(v)).hexdigest()\n\
          \tdef l():A=L(e[I].get(E,[]));A+=[{P:H+A2,G:e[q]},{P:H+A3,G:e[b]},{P:H+'stack-name',G:e[b].split('/')[1]},{P:H+A4,G:Y(e[I])}];D.add_tags_to_certificate(**{F:e[B],E:A})\n\
          \tdef Z():\n\t\tC(e);A=w(v(e['ResponseURL'],T(e),{'content-type':O},method='PUT'))\n\
          \t\tif A.status!=200:raise x(A)\n\ttry:\n\t\tz=Y(e['RequestId']+e[b]);A=e[I];D=J(A6,region_name=A.get(m));e[Q]='SUCCESS'\n\
          \t\tif e[d]=='Create':\n\t\t\tif K not in e:e[B]=A7;g()\n\t\t\tj()\n\t\t\
          \tif not i():return h()\n\t\telif e[d]==A8:\n\t\t\tif e[B]!=A7:\n\t\t\t\t\
          if e[B].startswith('arn:'):W(e[B])\n\t\t\t\telse:W(X(A))\n\t\telif e[d]==A9:\n\
          \t\t\tif y():\n\t\t\t\tC(A9)\n\t\t\t\tif X(A)==e[B]:\n\t\t\t\t\ttry:D=J(A6,region_name=e[AA].get(m));C(A8);W(X(e[AA]))\n\
          \t\t\t\t\texcept:R(O)\n\t\t\t\t\treturn Z()\n\t\t\t\tif K not in e:g()\n\
          \t\t\t\tj()\n\t\t\t\tif not i():return h()\n\t\t\telse:\n\t\t\t\tif E in\
          \ e[r+I]:D.remove_tags_from_certificate(**{F:e[B],E:e[r+I][E]})\n\t\t\t\t\
          l()\n\t\telse:raise U(e[d])\n\t\treturn Z()\n\texcept x as A0:R(O);e[Q]=A5;e['Reason']=str(A0);return\
          \ Z()"
      Description: Cloudformation custom resource for DNS validated certificates
      Handler: index.handler
      Role: !GetAtt 'CustomAcmCertificateLambdaExecutionRole.Arn'
      Runtime: python3.6
      Timeout: 900
      Tags:
        - Key: AppID
          Value: !Ref pAppID
        - Key: Environment
          Value: !Ref pEnv
        - Key: Notify
          Value: !Ref pNotify
        - Key: Order
          Value: !Ref pOrderNumber
        - Key: Org
          Value: !Ref pOrg
        - Key: AppName
          Value: !Ref pAppName
        - Key: Owner
          Value: !Ref pCFNOwnerTag
        - Key: Compliance
          Value: None
        - Key: DataClassification
          Value: Confidential
        - Key: CRIS
          Value: N/A
        - Key: MCP
          Value: "noMcp"
    Type: AWS::Lambda::Function

  CustomAcmCertificateLambdaExecutionRole:
    Properties:
      Tags:
        - Key: AppID
          Value: !Ref pAppID
        - Key: Environment
          Value: !Ref pEnv
        - Key: Notify
          Value: !Ref pNotify
        - Key: Order
          Value: !Ref pOrderNumber
        - Key: Org
          Value: !Ref pOrg
        - Key: AppName
          Value: !Ref pAppName
        - Key: Owner
          Value: !Ref pCFNOwnerTag
        - Key: Compliance
          Value: None
        - Key: DataClassification
          Value: Confidential
        - Key: CRIS
          Value: N/A
        - Key: MCP
          Value: "noMcp"
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: '2012-10-17'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - acm:AddTagsToCertificate
                  - acm:DeleteCertificate
                  - acm:DescribeCertificate
                  - acm:RemoveTagsFromCertificate
                Effect: Allow
                Resource:
                  - !Sub 'arn:aws:acm:*:${AWS::AccountId}:certificate/*'
              - Action:
                  - acm:RequestCertificate
                  - acm:ListTagsForCertificate
                  - acm:ListCertificates
                Effect: Allow
                Resource:
                  - '*'
              - Action:
                  - route53:ChangeResourceRecordSets
                Effect: Allow
                Resource:
                  - arn:aws:route53:::hostedzone/*
              - Action:
                  - sts:AssumeRole
                Resource:
                  - !Ref pRoute53AssumedRoleArn
                Effect: Allow    
            Version: '2012-10-17'
          PolicyName: !Sub '${AWS::StackName}CustomAcmCertificateLambdaExecutionPolicy'
    Type: AWS::IAM::Role

  generatedCertificate:
    Properties:
      DomainName: !Ref pWebsiteFQDN
      SubjectAlternativeNames: 
        - geomartcloud-qa.nonprod.pge.com
      DomainValidationOptions:
        - DomainName: !Ref pWebsiteFQDN
          HostedZoneId: !Ref pHostedZoneId
          Route53RoleArn: !Ref pRoute53AssumedRoleArn
        - DomainName: !Ref pWebsiteFQDN1
          HostedZoneId: !Ref pHostedZoneId
          Route53RoleArn: !Ref pRoute53AssumedRoleArn
      Tags:
        - Key: AppID
          Value: !Ref pAppID
        - Key: Environment
          Value: !Ref pEnv
        - Key: Notify
          Value: !Ref pNotify
        - Key: Order
          Value: !Ref pOrderNumber
        - Key: Org
          Value: !Ref pOrg
        - Key: AppName
          Value: !Ref pAppName
        - Key: Owner
          Value: !Ref pCFNOwnerTag
        - Key: Compliance
          Value: None
        - Key: DataClassification
          Value: Confidential
        - Key: CRIS
          Value: N/A
        - Key: MCP
          Value: "noMcp"
      ValidationMethod: DNS
      Region: us-east-1
      ServiceToken: !GetAtt 'CustomAcmCertificateLambda.Arn'
    Type: Custom::DNSCertificate

What is pWebsiteFQDN and pWebsiteFQDN1 set to?

pWebsiteFQDN and pWebsiteFQDN1 are set to hawctst.nonprod.pge.com and geomartcloud-tst.nonprod.pge.com respectively.

You have DomainValidationOptions for zones hawctst.nonprod.pge.com and geomartcloud-tst.nonprod.pge.com, but not one for geomartcloud-qa.nonprod.pge.com. The error is telling you that there should be a DomainValidationOptions for geomartcloud-qa.nonprod.pge.com, but it is missing.

@dflook , looks like there is typo. I think it would be better if I can give more information during troubleshooting session.
Can you please kindly let me know which time works for you. Thank you.

@RekhaRavula for commercial support queries please contact me via email at daniel@flook.org