El problema del mal uso de los Service Principals
Los Service Principals, en este sentido son algo parecidos a los usuarios de Azure AD, pero hay matices. Por poner un ejemplo, un Service Principal puede tener asociado ninguna, una, o varias contraseñas (cada una con su propia expiración), y además también podría tener asociado ninguno, uno, o varios certificados (igualmente, cada uno con su propia expiración). Esto permite, que diferentes personas, procesos, o aplicaciones, puedan utilizar el mismo Service Principal, pero autenticándose de forma diferente, y en consecuencia, pudiendo revocarles el acceso por separado. Podrías crear una contraseña nueva temporal de un día (sin tocar las existentes para no impactar) para hacer una prueba o resolver una incidencia, etc. Interesante. Esto mola.
Por el contrario, un usuario de Azure AD puede tener asociado una autenticación de doble factor (como un SMS a su móvil de empresa), que garantice la identidad de la persona que intenta realizar el acceso. Esto también mola. Un usuario de tu empresa, puede acceder a los recursos que necesita, porque tiene permisos, y sabes que es él por la autenticación de doble factor. Además, como administrador, puedes forzar la renovación de contraseñas. Tiene su puntito de seguridad, aunque como todo, hay que conocérselo muy bien.
Aquí llega el sabor agridulce de los Service Principals. Ahora yo dejo mi empresa, me deshabilitan mi usuario, y pierdo el acceso a los recursos… pero sorpresa, que tengo una excel con todos los Service Principals de mi proyecto, así que no pasa nada, desde casita, me conecto a donde quiero y cuando me da la gana. Bueno, quizás desde una red pública, o robando una Wifi, para que no sea tan fácil que me identifiquen. Esto ya no mola. Pero nada. Y además se aplica a más cosas, como las Key y los Tokens de las Storage Accounts, etc., aunque en este caso nos centraremos sólo en los Service Principals, que en muchos casos, no caducan, o caducan en un año o más.
La conclusión es clara. Los Service Principals son una responsabilidad, y es muy importante gestionarlos bien, y reciclar sus credenciales de acceso.
Una solución candidata
Es importante seguir un mínimo de buenas prácticas, para conseguir tener bien gestionados nuestros Service Principals.
- Usar un Service Principal para cada cosa, con los permisos mínimos necesarios.
- Almacenar sus contraseñas o certificados en Azure Key Vault.
- Inventariar los Service Principals, en qué Key Vault se utiliza, su propósito, etc.
- Y lo más importante: reciclarlos
Una aproximación, podría ser tener un proceso, que cada día genere nuevas contraseñas (ej: que expiren en 7 días) a los Service Principals, y las guarde en Azure Key Vault. Los procesos y los usuarios que necesiten utilizar los Service Principals, tendrán que acudir al Azure Key Vault. Cada día tendremos una contraseña diferente, pero como dicha contraseña expira en 7 días, si lanzo un proceso largo de más de 24h en el que utilizo el mismo Service Principal, no tendré problema. Además, si una persona deja la compañía, aunque tenga en una Excel las contraseñas de los Service Principals, ya se puede dar prisa en hacer maldades, porque todos expirarán como mucho en 7 días. En cierto modo, estamos tokenizando su acceso. No es ideal, pero infinitamente mejor que no rotar las contraseñas, y además, también podríamos acortar la expiración y el rotado, según nuestro nivel de paranoia.
En esta aproximación, sigue habiendo riesgos, pero están más controlados. Por ejemplo, el Service Principal que se encargue de cambiar las contraseñas del resto de Service Principals, en caso de verse comprometido, tenemos un riesgo alto, por lo que será necesario tratarle con especial atención. Sin embargo, el resto de Service Principals, estarían más protegidos.
Análisis técnico y primeros problemas
Supongamos que nos ha convencido esta aproximación, y lo queremos implementar en nuestra compañía. Vamos a ver con qué piedras nos encontramos en el camino.
Por defecto un Service Principal (llamémosle Padre) no puede crear otro Service Principal, ni puede crear/cambiar/borrar la contraseña de otro Service Principal, ni puede conceder permisos sobre un recurso de Azure (aunque sea propietario de dicho recurso)
Esto lo podemos solucionar dando los siguientes permisos a un Service Principal (el Padre), de tal modo que podrá crear nuevos Service Principals (de los cuales será Owner), y además al ser Owner de ellos, les podrá cambiar la contraseña. Para los Service Principals que ya existían, tendremos que configurarles para añadirles como Owner a nuestro Service Principal (Padre).
Los permisos que tendremos que dar al Service Principal, sobre la API de Windows Azure Active Directory, son los siguientes, y necesitaremos consentimiento de un Administrador de Azure AD:
- Application Permission - Read directory data
- Application Permission - Manage apps that this app creates or owns
- Delegated Permission - Sign in and read user profile
No podemos añadir como Owner de un Service Principal a otro Service Principal
Bueno, si podemos. Desde el Portal no se puede, o al menos, yo no he encontrado la opción. Pero sí es posible mediante código, siempre que tengas permisos suficientes (ej: seas owner del Service Principal al que quieres añadir el nuevo Owner). De este modo, un Service Principal puede tener varios Owners, su creador, y otros que añadamos para delegar su administración.
A continuación se muestra un ejemplo con Azure CLI, teniendo en cuenta que: --owner-object-id es el Object Id del Service Principal (el Padre) asociado al App Registration (lo podemos obtener ejecutando algo como az ad sp show --id 1f60bf1a-6a59-4d6b-bb4a-6369152a2e7b pero especificando el App Id del Padre), y --id es el Object Id ó App Id del App Registration del Hijo
az ad app owner add --id c75d6721-a9af-4d24-bd9e-4d2718501bc7 --owner-object-id 55438bd7-195a-4435-9392-7e7de3dd8041 |
Nota aclaratoria. Aunque estamos hablando todo el rato de Service Principals, en la práctica, lo que se hace es crear un App Registration, que tiene asociado un Service Principal, es decir, se trata de dos objetos diferentes, aunque relacionados. El App Id en una propiedad del App Registration, y ambos objetos tienen una propiedad Object Id. Por eso, tenemos que tener claro, que Object Id necesitamos, el del App Id o el del Service Principal. Esto genera algo de confusión, porque muchas veces acabamos hablando de Service Principals, para referirnos indistintavemente a uno u otro.
Permisos necesarios en las Subscripciones de Azure
Para poder crear un Service Principal, necesitaremos tener permisos en la Subscripción de Azure. En las pruebas que hemos realizado, al Service Principal (el Padre) que hemos usado para crear otros Service Principal, le hemos dado permisos de Read en la Subscripción. Con esto, al intentar crear un Service Principal, mostrará un mensaje de error, pero lo creará. Si damos permisos de Owner en la Subscripción, al crear el Service Principal desde Azure CLI, le hará Contributor de la Subscripción, algo que queremos evitar.
Expiración de las Contraseñas de los Service Principals
Por defecto las contraseñas de un Service Principal caducan al año, al crearlas desde Azure CLI. Si bien, con Azure CLI sólo es posible especificar múltiplos de 1 año, tendríamos la posibilidad de borrar las contraseñas antiguas. Otra alternativa, crearlas desde Powershell.
Al intentar cambiar crear una nueva contraseña obtenemos el error Update to existing credential with KeyId 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' is not allowed
Este error lo he encontrado al intentar crear un nueva contraseña con --append y la solución fué crear un nueva contraseña sin --append, después de lo cual, podía crear nuevas contraseñas tanto con --append como sin él. Un poco raro, la verdad, pero al menos con este fix, problema solucionado.
Versión de Azure CLI
Para que todo esto nos funcione, necesitamos tener una versión reciente de Azure CLI.
Depurar la ejecución de comandos Azure CLI
Si nos atascamos, en ocasiones resulta útil el parámetro --debug de los comandos az
Ejemplo de código
Y ya para terminar, sólo queda añadir algún ejemplo de código, en este caso con Azure CLI, para que nos sea más fácil poner todo esto en práctica.
# Logarse con un Service Principal az login --service-principal -u "2f60bf1a-6a59-4d6b-bb4a-636915ba2f7b" -p "iQIsa2ZcLEwXc6eK1vSzssnEJWjLZ5vHvRKLwGxM==" --tenant "a737a526-0f4a-4f8e-a1ed-58c951d983d5" --subscription a5e0c2ff-ab76-44be-a513-9a6e38d48f47
# Mostrar el Service Principal asociado al App Registration (observar el ObjectId) az ad sp show --id 2f60bf1a-6a59-4d6b-bb4a-636915ba2f7b
# Crear un Service Principal az ad sp create-for-rbac -n "sq_guillesql"
# Listar Service Principals az ad sp list
# Mostrar un Service Principal az ad sp show --id f75d67a1-a96f-4d24-bd9e-4d2718501bc0
# Dar permisos a un Service Principal az role assignment create --scope /subscriptions/a5e0c2ff-ab76-44be-a513-9a6e38d48f47/resourceGroups/guillesql --role Reader --assignee f75d67a1-a96f-4d24-bd9e-4d2718501bc0
# Listar constraseñas de un Service Principal az ad sp credential list --id f75d67a1-a96f-4d24-bd9e-4d2718501bc0
# Borrar una contraseña de un Service Principal az ad sp credential delete --id f75d67a1-a96f-4d24-bd9e-4d2718501bc0 --key-id 288d1adc-fa1f-474a-bc54-9ffcdf0ffc40
# Crear nueva contraseña (aleatoria) a un Service Principal, con un año de validez az ad sp credential reset --name "sq_guillesql" --append --years 1 --credential-description "first-password"
# Crear nueva contraseña (pre-establecida) a un Service Principal, con un año de validez (DEPRECATED) az ad sp credential reset --name "sq_guillesql" --append --years 1 --credential-description "first-password" --password 12.34.56.789.Ab
# Añadir un Owner (de tipo Service Principal) a un Service Principal del que ya se es Owner # --owner-object-id (el padre) => Object Id del Service Principal asociado al App Registration # --id (el hijo) => Object Id del App Registration az ad app owner add --id f75d67a1-a96f-4d24-bd9e-4d2718501bc0 --owner-object-id 25438bd7-195a-4435-9392-7e7de3dd804d
# Hacer a un Service Principal owner de sí mismo # Nota: Este comando tiene que se ejecutado por un usuario o Service Principal que sea Owner del Service Principal al que se le quiere añadir un nuevo Owner az ad app owner add --id 2f60bf1a-6a59-4d6b-bb4a-636915ba2f7b --owner-object-id 46e0ca5e-53d3-4e79-9782-7d96e7506f47
# Borrar un Service Principal az ad sp delete --id 357933e5-6a84-4580-957d-7e855da8fd81
|
Poco más por hoy. Como siempre, confío que la lectura resulte de interés.