Azure App Service からプライベートエンドポイント経由の Azure SQL Database へパスワードレス Entra 認証を試してみた
今でも環境変数にパスワードを設定してプログラムから利用するの事があるのですが、時代の流れはパスワードレス。
プログラムからパスワードレスで Azure リソースにアクセスできる、マネージド ID が以前からあります。
このマネージド ID を使用して、Azure SQL Dataase への接続をパスワードレスで認証する事ができます。
そこで、Azure App Service の通常盤とコンテナー版でそれぞれ sqlcmd を使用して、インフラ的な動作を確認してみました。
Azure App Sercice 通常盤の検証環境を構築
region=japaneast
prefix=mnrappsql
az group create \
--name ${prefix}-rg \
--location $region
az network vnet create \
--name ${prefix}-vnet \
--resource-group ${prefix}-rg \
--address-prefixes 10.0.0.0/24
az network nsg create \
--resource-group ${prefix}-rg \
--name ${prefix}-nsg
az network vnet subnet create \
--name app-subnet \
--vnet-name ${prefix}-vnet \
--resource-group ${prefix}-rg \
--address-prefixes 10.0.0.0/26 \
--network-security-group ${prefix}-nsg
az network vnet subnet create \
--name sql-subnet \
--vnet-name ${prefix}-vnet \
--resource-group ${prefix}-rg \
--address-prefixes 10.0.0.64/26 \
--network-security-group ${prefix}-nsg \
--disable-private-endpoint-network-policies true
az appservice plan create \
--name ${prefix}-plan \
--resource-group ${prefix}-rg \
--is-linux \
--sku B1
az webapp create \
--name ${prefix}-app \
--resource-group ${prefix}-rg \
--plan ${prefix}-plan \
--assign-identity \
--runtime "JAVA:17-java17"
az webapp config set \
--name ${prefix}-app \
--resource-group ${prefix}-rg \
--always-on true
az webapp config access-restriction add \
--name ${prefix}-app \
--resource-group ${prefix}-rg \
--priority 100 \
--rule-name MyIP \
--action Allow \
--ip-address $(curl -s inet-ip.info)
az webapp vnet-integration add \
--name ${prefix}-app \
--resource-group ${prefix}-rg \
--vnet ${prefix}-vnet \
--subnet app-subnet
# external-admin-sid は自分の Entra ユーザーオブジェクト ID に変更
az sql server create \
--enable-ad-only-auth \
--external-admin-principal-type User \
--external-admin-name myname \
--external-admin-sid 9ae16906-c0ff-4599-9889-d67e4895d086 \
--name ${prefix}-sqlsrv \
--resource-group ${prefix}-rg
az sql db create \
--resource-group ${prefix}-rg \
--server ${prefix}-sqlsrv \
--name sqldb \
--service-objective Basic \
--backup-storage-redundancy Local
az sql server firewall-rule create \
--resource-group ${prefix}-rg \
--server ${prefix}-sqlsrv \
--name LocalAccess \
--start-ip-address $(curl -s inet-ip.info) \
--end-ip-address $(curl -s inet-ip.info)
az network private-endpoint create \
--name ${prefix}-pe \
--resource-group ${prefix}-rg \
--vnet-name ${prefix}-vnet \
--subnet sql-subnet \
--private-connection-resource-id $(az sql server show \
--resource-group ${prefix}-rg \
--name ${prefix}-sqlsrv \
--query id \
--output tsv) \
--group-ids sqlServer \
--connection-name ${prefix}-conn
az network private-dns zone create \
--resource-group ${prefix}-rg \
--name privatelink.database.windows.net
az network private-dns link vnet create \
--resource-group ${prefix}-rg \
--zone-name privatelink.database.windows.net \
--name ${prefix}-link \
--virtual-network ${prefix}-vnet \
--registration-enabled false
az network private-endpoint dns-zone-group create \
--resource-group ${prefix}-rg \
--endpoint-name ${prefix}-pe \
--name ${prefix}-zg \
--private-dns-zone privatelink.database.windows.net \
--zone-name sql
Azure ポータルの SQL Database クエリエディターから App Service のマネージド ID を許可
CREATE USER [mnrappsql-app] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [mnrappsql-app];
GO
Azure ポータルの App Service の SSH から動作確認
apt update
apt install -y dnsutils bzip2 curl jq
# 名前解決の確認
nslookup mnrappsql-sqlsrv.database.windows.net
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
mnrappsql-sqlsrv.database.windows.net canonical name = mnrappsql-sqlsrv.privatelink.database.windows.net.
Name: mnrappsql-sqlsrv.privatelink.database.windows.net
Address: 10.0.0.68
# 環境変数の確認
env | grep IDENTITY
IDENTITY_HEADER=5021c390-f002-439e-9329-4755fd72da45
IDENTITY_ENDPOINT=http://169.254.130.3:8081/msi/token
# Azure Instance Metadata Service の確認
curl -H x-identity-header:$IDENTITY_HEADER \
"$IDENTITY_ENDPOINT?resource=https://database.windows.net&api-version=2019-08-01" \
| jq .
{
"access_token": "eyJ0e..........................SxYGQ",
"expires_on": "1739660871",
"resource": "https://database.windows.net",
"token_type": "Bearer",
"client_id": "1449f973-6cff-4d90-b184-ee0648cc2523"
}
# マネージド ID 認証対応版 sqlcmd をダウンロード
wget https://github.com/microsoft/go-sqlcmd/releases/download/v1.8.2/sqlcmd-linux-amd64.tar.bz2
tar xvfj sqlcmd-linux-amd64.tar.bz2
# sqlcmd でマネージド ID 認証を使用して接続
./sqlcmd \
-S mnrappsql-sqlsrv.privatelink.database.windows.net \
-d sqldb \
--authentication-method ActiveDirectoryManagedIdentity
# SQL 実行
select @@version;
go
exit
Azure App Sercice コンテナー版の検証環境を構築
az acr create \
--name ${prefix}acr \
--resource-group ${prefix}-rg \
--sku Basic \
--admin-enabled true
mkdir ${prefix} && cd ${prefix}
cat <<"EOF" > Dockerfile
FROM ubuntu/nginx
RUN date > /var/www/html/index.html
ENV SSH_PASSWD "root:Docker!"
RUN apt-get update \
&& apt-get install -y --no-install-recommends dialog \
&& apt-get update \
&& apt-get install -y --no-install-recommends openssh-server \
&& echo "$SSH_PASSWD" | chpasswd
COPY sshd_config /etc/ssh/
COPY init.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/init.sh
EXPOSE 80 2222
ENTRYPOINT ["init.sh"]
EOF
cat <<"EOF" > sshd_config
Port 2222
ListenAddress 0.0.0.0
LoginGraceTime 180
X11Forwarding yes
Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr
MACs hmac-sha1,hmac-sha1-96
StrictModes yes
SyslogFacility DAEMON
PasswordAuthentication yes
PermitEmptyPasswords no
PermitRootLogin yes
EOF
cat <<"EOF" > init.sh
#!/bin/bash
set -e
eval $(printenv | sed -n "s/^\([^=]\+\)=\(.*\)$/export \1=\2/p" | sed 's/"/\\\"/g' | sed '/=/s//="/' | sed 's/$/"/' >> /etc/profile)
service ssh start
nginx -g "daemon off;"
EOF
az acr build \
--resource-group ${prefix}-rg \
--registry ${prefix}acr \
--image ${prefix} \
.
az webapp create \
--name ${prefix}-web \
--resource-group ${prefix}-rg \
--plan ${prefix}-plan \
--assign-identity \
--deployment-container-image-name ${prefix}acr.azurecr.io/${prefix}:latest \
--docker-registry-server-user ${prefix}acr \
--docker-registry-server-password $(az acr credential show \
--resource-group ${prefix}-rg \
--name ${prefix}acr \
--query "passwords[0].value" \
--output tsv)
az webapp config set \
--name ${prefix}-web \
--resource-group ${prefix}-rg \
--always-on true
az webapp config access-restriction add \
--name ${prefix}-web \
--resource-group ${prefix}-rg \
--priority 100 \
--rule-name MyIP \
--action Allow \
--ip-address $(curl -s inet-ip.info)
az webapp vnet-integration add \
--name ${prefix}-web \
--resource-group ${prefix}-rg \
--vnet ${prefix}-vnet \
--subnet app-subnet
Azure ポータルの SQL Database クエリエディターから追加した App Service のマネージド ID を許可
CREATE USER [mnrappsql-web] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [mnrappsql-web];
GO
Azure ポータルの App Service の SSH からコンテナー版の動作確認
apt update
apt install -y dnsutils bzip2 curl jq wget
nslookup mnrappsql-sqlsrv.database.windows.net
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
mnrappsql-sqlsrv.database.windows.net canonical name = mnrappsql-sqlsrv.privatelink.database.windows.net.
Name: mnrappsql-sqlsrv.privatelink.database.windows.net
Address: 10.0.0.68
env | grep IDENTITY
IDENTITY_HEADER=da89ad08-dbf1-4852-b920-c672389c3b9d
IDENTITY_ENDPOINT=http://169.254.131.5:8081/msi/token
curl -H x-identity-header:$IDENTITY_HEADER \
"$IDENTITY_ENDPOINT?resource=https://database.windows.net&api-version=2019-08-01" \
| jq .
{
"access_token": "eyJ0e..........................T66mHg",
"expires_on": "1739661867",
"resource": "https://database.windows.net",
"token_type": "Bearer",
"client_id": "18167057-ac51-4a5b-813b-1b766ec3668d"
}
wget https://github.com/microsoft/go-sqlcmd/releases/download/v1.8.2/sqlcmd-linux-amd64.tar.bz2
tar xvfj sqlcmd-linux-amd64.tar.bz2
./sqlcmd \
-S mnrappsql-sqlsrv.privatelink.database.windows.net \
-d sqldb \
--authentication-method ActiveDirectoryManagedIdentity
select @@version;
go
exit
参考
Azure Web App for Containers で Azure Container Registry と連携したお手軽 CI/CD を試してみた