Azure 证书自动化管理解决方案

节前不打烊,以此小文提前预祝大家鼠年大吉。最近和小伙伴们沟通发现,经常有系统出现证书过期的问题,究其原因主要是1. 证书后台自动 renew 了但是系统并没有及时更新 2. 因为没有邮件提醒所以忽略的证书过期。针对上述问题,目前在 Azure 平台内大部分的 PaaS 服务都已经内置了证书自动更新的能力,但对于 IaaS 服务,应为管理界面从操作系统开始都掌握在客户自己手上,所以操作系统内应用的证书更新需要客户自己来完成,那么 Azure 平台有什么好的方法可以帮助客户避免证书过期的问题。本文将介绍如何通过 Azure 平台的服务实现证书过期发现及自动更新。

证书过期预警:

Azure 平台的 Key Vault 服务提供了证书托管服务,客户可以将自己的管理的证书上传至 Key Vault 服务,然后通过设置 Issuance Policy,通过在其中设置 Lifetime Action Type 来实现证书过期邮件提醒,可参阅:https://docs.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates#key-vault-certificates

证书自动更新:

Azure 平台的 Key Vault 服务支持对自签名或支持的集成证书提供方 (DigiCert, GlobalSign) 提供自动证书 Renew 服务,借助该功能可实现无干预的证书自动续签。但是类似虚拟机 IaaS 服务内的应用服务证书更新仍然需要用户自己来处理完成。这部分的自动化可以通过 Event Grid + Logic App 来实现。Azure Event Grid 服务原生集成支持 Key Vault 服务的事件消息,当有证书续签的时候会释放证书事件消息,Logic App 通过订阅监听改证书事件消息可以实现后续证书更细的自动化流程。本文帮助大家更快上手选择了 Logic App(基本上可以是零代码),喜欢代码解决问题的同学可以考虑使用 Function 服务。本文的例子中以更新虚拟机中 Nginx 服务其中的 SSL 证书为例,在 Logic App 中通过调用 Azure Resource Management -- Resource Update 模块来将续签好的证书上传至虚拟机,这里需要强调微软云的 VM 原生的模板中已经支持证书属性,该属性可以通过指定 Azure Key Vault 中存放的证书地址实现将证书上传至虚拟机中,这样就实现了 Azure Resource Template 来自动上传证书。上传证书后再调用 Azure Resource Management -- Resource Update 来调用 VM 的 customscript extension 扩展模块,通过执行脚本来实现将上传好的续签证书来自动更新 Nginx SSL 证书存放路径的过期证书。下面我们来看一下方案的架构图:

上图中还引入了 Azure Table,主要是通过基础的 KV 服务实现多应用/多主机证书自动更新的支持,在 Azure Table 中存储 KeyVault 名称,证书名称和虚拟机的对应关系,这样当 Event Grid 中 Fire 事件后,可以通过在 Table 中查询到与该事件相关证书与什么虚拟机相关。

Logic App 里面三个模块的具体实现这里不再做详细的赘述,基本实现逻辑在上面已经介绍,大家可以参照下面的实例模板代码进行导入修改。其中资源组名称,作为初始变量,大家可以根据自己的实际环境进行调整。

{
    "definition": {
        "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
        "actions": {
            "Compose_cert_template": {
                "inputs": {
                    "osProfile": {
                        "secrets": [
                            {
                                "sourceVault": {
                                    "id": "@concat(‘/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/‘,variables(‘resourcegroupname‘),‘/providers/Microsoft.KeyVault/vaults/‘,body(‘Parse_JSON‘)?[‘vaultName‘])"
                                },
                                "vaultCertificates": [
                                    {
                                        "certificateUrl": "@concat(‘https://‘,body(‘Parse_JSON‘)?[‘vaultName‘],‘.vault.azure.net/secrets/‘,body(‘Parse_JSON‘)?[‘objectName ‘],‘/‘,body(‘Parse_JSON‘)?[‘version‘])"
                                    }
                                ]
                            }
                        ]
                    }
                },
                "runAfter": {
                    "Initialize_vmname_variable": [
                        "Succeeded"
                    ]
                },
                "type": "Compose"
            },
            "Delay": {
                "inputs": {
                    "interval": {
                        "count": 30,
                        "unit": "Second"
                    }
                },
                "runAfter": {
                    "Update_Application_certificate": [
                        "Succeeded"
                    ]
                },
                "type": "Wait"
            },
            "Get_entity": {
                "inputs": {
                    "host": {
                        "connection": {
                            "name": "@parameters(‘$connections‘)[‘azuretables‘][‘connectionId‘]"
                        }
                    },
                    "method": "get",
                    "path": "/Tables/@{encodeURIComponent(‘vmcertmapping‘)}/entities(PartitionKey=‘@{encodeURIComponent(body(‘Parse_JSON‘)?[‘vaultName‘])}‘,RowKey=‘@{encodeURIComponent(body(‘Parse_JSON‘)?[‘objectName ‘])}‘)"
                },
                "runAfter": {
                    "Parse_JSON": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection"
            },
            "Initialize_resourcegroup_variable": {
                "inputs": {
                    "variables": [
                        {
                            "name": "resourcegroupname",
                            "type": "string",
                            "value": "akvdemo"
                        }
                    ]
                },
                "runAfter": {},
                "type": "InitializeVariable"
            },
            "Initialize_vmname_variable": {
                "inputs": {
                    "variables": [
                        {
                            "name": "vmname",
                            "type": "string",
                            "value": "@{body(‘Get_entity‘)?[‘vmname‘]}"
                        }
                    ]
                },
                "runAfter": {
                    "Get_entity": [
                        "Succeeded"
                    ]
                },
                "type": "InitializeVariable"
            },
            "Parse_JSON": {
                "inputs": {
                    "content": "@triggerBody()?[‘data‘]",
                    "schema": {
                        "properties": {
                            "Id": {
                                "type": "string"
                            },
                            "exp": {
                                "type": "string"
                            },
                            "nbf": {
                                "type": "string"
                            },
                            "objectName ": {
                                "type": "string"
                            },
                            "objectType": {
                                "type": "string"
                            },
                            "vaultName": {
                                "type": "string"
                            },
                            "version": {
                                "type": "string"
                            }
                        },
                        "type": "object"
                    }
                },
                "runAfter": {
                    "Initialize_resourcegroup_variable": [
                        "Succeeded"
                    ]
                },
                "type": "ParseJson"
            },
            "Refresh_Customextension": {
                "inputs": {
                    "body": {
                        "location": "eastus",
                        "properties": {
                            "autoUpgradeMinorVersion": true,
                            "publisher": "Microsoft.Azure.Extensions",
                            "settings": {
                                "commandToExecute": "date",
                                "fileUris": []
                            },
                            "type": "CustomScript",
                            "typeHandlerVersion": "2.0"
                        }
                    },
                    "host": {
                        "connection": {
                            "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"
                        }
                    },
                    "method": "put",
                    "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(variables(‘resourcegroupname‘))}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘virtualMachines/‘,variables(‘vmname‘),‘/extensions/customScript‘))}",
                    "queries": {
                        "x-ms-api-version": "2019-03-01"
                    }
                },
                "runAfter": {
                    "Delay": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection"
            },
            "Update_Application_certificate": {
                "inputs": {
                    "body": {
                        "location": "eastus",
                        "properties": {
                            "autoUpgradeMinorVersion": true,
                            "publisher": "Microsoft.Azure.Extensions",
                            "settings": {
                                "commandToExecute": "sh config-cert.sh",
                                "fileUris": [
                                    "https://akvdemoscript.blob.core.windows.net/customscript/config-cert.sh"
                                ]
                            },
                            "type": "CustomScript",
                            "typeHandlerVersion": "2.0"
                        }
                    },
                    "host": {
                        "connection": {
                            "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"
                        }
                    },
                    "method": "put",
                    "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(variables(‘resourcegroupname‘))}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘virtualMachines/‘,variables(‘vmname‘),‘/extensions/customScript‘))}",
                    "queries": {
                        "x-ms-api-version": "2019-03-01"
                    }
                },
                "runAfter": {
                    "Update_VM_certificate": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection"
            },
            "Update_VM_certificate": {
                "inputs": {
                    "body": {
                        "location": "eastus",
                        "properties": "@outputs(‘Compose_cert_template‘)"
                    },
                    "host": {
                        "connection": {
                            "name": "@parameters(‘$connections‘)[‘arm‘][‘connectionId‘]"
                        }
                    },
                    "method": "put",
                    "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/resourcegroups/@{encodeURIComponent(variables(‘resourcegroupname‘))}/providers/@{encodeURIComponent(‘Microsoft.Compute‘)}/@{encodeURIComponent(concat(‘virtualMachines/‘,variables(‘vmname‘)))}",
                    "queries": {
                        "x-ms-api-version": "2019-03-01"
                    }
                },
                "runAfter": {
                    "Compose_cert_template": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection"
            }
        },
        "contentVersion": "1.0.0.0",
        "outputs": {},
        "parameters": {
            "$connections": {
                "defaultValue": {},
                "type": "Object"
            }
        },
        "triggers": {
            "When_a_resource_event_occurs": {
                "inputs": {
                    "body": {
                        "properties": {
                            "destination": {
                                "endpointType": "webhook",
                                "properties": {
                                    "endpointUrl": "@{listCallbackUrl()}"
                                }
                            },
                            "filter": {
                                "includedEventTypes": [
                                    "Microsoft.KeyVault.CertificateNewVersionCreated"
                                ]
                            },
                            "topic": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/akvdemo/providers/Microsoft.KeyVault/vaults/akvdemorepo"
                        }
                    },
                    "host": {
                        "connection": {
                            "name": "@parameters(‘$connections‘)[‘azureeventgrid‘][‘connectionId‘]"
                        }
                    },
                    "path": "/subscriptions/@{encodeURIComponent(‘c04b3c63-8dfe-4f98-be18-e71ff67a1f4e‘)}/providers/@{encodeURIComponent(‘Microsoft.KeyVault.vaults‘)}/resource/eventSubscriptions",
                    "queries": {
                        "x-ms-api-version": "2017-09-15-preview"
                    }
                },
                "splitOn": "@triggerBody()",
                "type": "ApiConnectionWebhook"
            }
        }
    },
    "parameters": {
        "$connections": {
            "value": {
                "arm": {
                    "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/akvdemo/providers/Microsoft.Web/connections/arm",
                    "connectionName": "arm",
                    "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/eastus/managedApis/arm"
                },
                "azureeventgrid": {
                    "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/akvdemo/providers/Microsoft.Web/connections/azureeventgrid",
                    "connectionName": "azureeventgrid",
                    "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/eastus/managedApis/azureeventgrid"
                },
                "azuretables": {
                    "connectionId": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/resourceGroups/akvdemo/providers/Microsoft.Web/connections/azuretables",
                    "connectionName": "azuretables",
                    "id": "/subscriptions/c04b3c63-8dfe-4f98-be18-e71ff67a1f4e/providers/Microsoft.Web/locations/eastus/managedApis/azuretables"
                }
            }
        }
    }
}

Azure VM 对于 Azure Key Vault 的证书原生支持,通过在 VM 描述 Template 描述属性中直接引用 Key Vault 证书路径,即可实现虚拟机对证书的上传导入, Azure Resource VM Template 示例可以参阅如下示例。

{
  "osProfile": {
    "secrets": [
      {
        "sourceVault": {
          "id": "@{keyvaultid}"
        },
        "vaultCertificates": "@{certificate secret id}"
          }
        ]
      }
    ]
  }
}

在 Logic App 中执行的 Custom Script Extension, 是通过执行已经预先上载在 Blob 服务中的脚本文件来实现应用证书更新的,这部分代码逻辑是按照 Nginx SSL 证书示例来做的,大家可以按照实际自己的应用证书路径进行修改。

#!/bin/bash

secretsname=$(find /var/lib/waagent/ -name "*.prv" | cut -c -57)
sudo cp $secretsname.crt /etc/nginx/ssl/demo.cert
sudo cp $secretsname.prv /etc/nginx/ssl/demo.prv
rm -f $secretsname.crt
rm -f $secretsname.prv
sudo service nginx restart
echo $secretsname

Azure Table 中的 Schema 非常简单,只包含一个 vmname 属性,如果大家有更复杂的 Mapping 逻辑可以按照自己的要求定义自己的 Schema。下面供大家参考。其中 PartitionKey 采用 KeyVault Name,RowKey 采用证书名称,该场景 entity 数量和访问频次都不会很多,所以这种 Partition 方式已经可以满足要求。

好了就写到这里啦,小伙伴们鼠年再见!

原文地址:https://www.cnblogs.com/wekang/p/12218952.html

时间: 2024-11-03 13:13:36

Azure 证书自动化管理解决方案的相关文章

使用Azure Automation(自动化)定时关闭和启动虚拟机

1. 概述 作为Windows Azure的用户,使用Azure的过程中,最担心的事情就是还没到月底,预设的费用就快消耗完了(下面两张账单图是我最讨厌看到的).但是仔细分析自己的费用列表,发现绝大部分费用消耗在虚拟机上,而Azure的虚拟机是按照开机时间来计费的,因此迫切需要找到一个方案来节省虚拟机的开销.最简单的方案就是在不需要的时候将虚拟机自动关闭,需要的时间让其自动开机.在Google了一通以后,发现可以通过Azure的自动化(Automation)功能达到上述目的.下面介绍我在Azure

使用Azure Automation Hybrid管理本地SQL Server备份状态(一)

Azure Automation是一种云解决方案,可通过自动执行任务,为服务器提供所需的状态配置以及配置管理来帮助组织满足其基础结构和安全性要求.默认情况下,创建Azure自动化后,它将允许在Azure中执行脚本.但是某些组织希望能够在其他云和本地环境中自动化任务,那么此时Hybrid Worker工具是提供这种解决方案的关键.利用 Azure Automation Hybrid worker功能,既可以直接在托管角色的计算机上运行 Runbook,也可以对环境中的资源运行 Runbook,从而

CDIF灵长科技 API 管理解决方案技术白皮书

灵长科技 API 管理解决方案的核心技术是具备中美知识产权保护的,名为通用设备互联框架(CDIF:common device interconnect framework)的软件框架,目前有部分开源实现存放在:https://github.com/out4b/cdif(注:开源部分遵守相关开源代码许可协议).CDIF 是世界上第一种基于 REST 和 JSON 的 SOA软件框架,提供了与基于XML 的 WSDL 语言和 SOAP 协议同等抽象能力.但简洁得多的基于JSON 的实现,也非常适合用

自动化管理工具puppet

Linux之puppet puppet简介 puppet是一套IT基础设施自动化管理工具,可以管理其整个生命周期,其官方网站:www.puppetlabs.org.其作者Luke Kanies成立了puppetLabs,于2005年发布0.2版本.puppet基于ruby语言研发,puppet有声明性.基于模型的配置语言,其也有自己的配置编程言. puppet的优势 基于master/agent的认证机制 不依赖于客户端系统的管理权限 可实现配置自动推送给客户端 puppet的版本 0.2 0.

智能时代 企业自动化管理升级

新经济环境下,随着众多创业公司如雨后春笋般拔地而起,许多企业开始有了危机意识,纷纷拓展业务,引进更多企业资源,以便在竞争激烈的市场中站稳脚跟,赢得更广阔的发展空间.随着业务扩张和发展,企业对自动化管理的需求越来越大,比如客户关系管理.项目管理.采购管理.日常办公管理等等,如果能够通过信息化技术进行自动管理,不但减少人力资源支出,而且各项业务管理也更清晰有序,提高工作效率.通过企业自动化管理,企业在内部管理和外部业务拓展方面将得到更进一步的发展.对此,企业应用管理软件成了企业向自动化管理转型的利器

ios7.1安装提示"无法安装应用程序 因为证书无效"的解决方案二(dropbox被封项目转移到Appharbor上)

6月18日起dropbox被天朝封了(这个真是无力吐槽),而ios7.1要求使用ssl安全连接,则需要重新找到一个支持https的免费服务器.Appharbor是个不错的选择,操作简单,此外需要添加配置文件来识别plist,ipa文件,有关如何使用Appharbor转自: 免费ASP和ASP.NET空间Webweb和.NET云计算空间Appharbor 一.Appharbor免费.NET云计算空间申请 PS:Appharbor官网: https://appharbor.com/ 1.进入Apph

发票管理解决方案

TaxPLUS发票管理解决方案是基于Hitpoint专属PLUS平台,基于本土最佳实践建立一套有效的发票管理系统,通过创新的模式将所有发票相关事务纳入其中,聚焦发票自动化.流程化.协同化管理,最终全面提升企业税务管理信息化水平和管理能力. Hitpoint TaxPLUS采用B/S架构,适用于财务共享中心集中管控开票.复杂多变开票需求的企业,支持多公司.多门店.多部门.多岗位协同工作. Tax PLUS发票管理解决方案特点 在中国,对发票的流程管理重点在发票的开具,了解.记录.追踪发票的开具流程

ansible自动化管理windows系统实战

一.简述 1.说明日常系统自动化运维过程中难免会有windows系列服务器,就开源软件来说目前大多的对windows批量管理兼容性不太好;不像Linux系统便捷,但现实中确实有些业务需要跑在windows上;搜索查找折腾一番后,发现python开发的ansible(已经被redhat收购)有比较好的解决方案,通过一番折腾,整理出来,以备忘交流; 2.实验环境服务器端:CentOS7.4_x64 自带python 2.7.5 ip:172.16.3.167源码安装ansible 被管理window

客户端单周发版下的多分支自动化管理与实践

背景 目前,互联网产品呈现出高频优化迭代的趋势,需求方希望尽早地看到结果,并给予及时反馈,所以技术团队需要用"小步快跑"的姿势来做产品,尽早地交付新版本.基于以上背景,美团客户端研发平台适时地推行了单周发版的迭代策略.单周版本迭代的优点可以概括为三个方面:更快地验证产品创意是否符合预期,更灵活地上线节奏,更早地修复线上Bug. 首先说一下美团平台的发版策略,主要变更点是由之前的每四周发一版改为每周都有发版.具体对比如下: (旧)三周迭代指的是2周开发+1周半测试,依赖固定的排期和测试时