diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d25cf1a..9125918 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,9 @@ jobs: name: Tests strategy: matrix: + force-encryption: + - "true" + - "false" os: # ignore ARM64 flavours - ubuntu-20.04 @@ -22,7 +25,12 @@ jobs: version: - 2017 exclude: - - os: ubuntu-24.04 + - force-encryption: "true" + os: ubuntu-24.04 + version: 2017 + + - force-encryption: "false" + os: ubuntu-24.04 version: 2017 runs-on: ${{ matrix.os }} @@ -39,6 +47,7 @@ jobs: uses: ./action with: components: sqlcmd,sqlengine + force-encryption: ${{ matrix.force-encryption }} sa-password: "bHuZH81%cGC6" version: ${{ matrix.version }} @@ -47,4 +56,5 @@ jobs: action/test.ps1 shell: pwsh env: + FORCE_ENCRYPTION: ${{ matrix.force-encryption }} SA_PASSWORD: "bHuZH81%cGC6" diff --git a/action.yml b/action.yml index 85f6377..f8577b7 100644 --- a/action.yml +++ b/action.yml @@ -7,6 +7,10 @@ inputs: components: description: "The components to install" required: true + force-encryption: + description: "Should the server force encryption?" + required: false + default: "false" sa-password: description: "The SA password for the SQL instance" required: true @@ -20,6 +24,7 @@ runs: run: | $params = @{ Components = ("${{ inputs.components }}" -split ",").Trim() + ForceEncryption = "${{ inputs.force-encryption }}" -eq "true" SaPassword = "${{ inputs.sa-password }}" Version = "${{ inputs.version }}" } diff --git a/install.ps1 b/install.ps1 index 4105189..bfc0d70 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,6 +1,7 @@ param ( [ValidateSet("sqlcmd", "sqlengine")] [string[]]$Components, + [bool]$ForceEncryption, [string]$SaPassword, [ValidateSet("2017")] [string]$Version @@ -9,7 +10,7 @@ param ( function Wait-ForContainer { $checkInterval = 5 $containerName = "sql" - $timeout = 120 + $timeout = 60 $startTime = Get-Date Write-Host "Waiting for the container '$containerName' to be healthy..." @@ -73,8 +74,31 @@ if ("sqlengine" -in $Components) { exit 1 } + if ($ForceEncryption) { + Write-Output "Force encryption is set, generating self-signed certificate ..." + + # SOURCE: https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-docker-container-security?view=sql-server-ver16#encrypt-connections-to-sql-server-linux-containers + & mkdir -p /opt/mssql + & openssl req -x509 -nodes -newkey rsa:2048 -subj '/CN=sql1.contoso.com' -keyout /opt/mssql/mssql.key -out /opt/mssql/mssql.pem -days 365 + $MssqlConf = @' +[network] +tlscert = /etc/ssl/certs/mssql.pem +tlskey = /etc/ssl/private/mssql.key +tlsprotocols = 1.2 +forceencryption = 1 +'@ + + Set-Content -Path /opt/mssql/mssql.conf -Value $MssqlConf + & sudo chmod -R 775 /opt/mssql + + Copy-Item -Path /opt/mssql/mssql.pem -Destination /usr/share/ca-certificates/mssql.crt + & sudo dpkg-reconfigure ca-certificates + + $AdditionalContainerConfiguration = "-v /opt/mssql/mssql.conf:/var/opt/mssql/mssql.conf -v /opt/mssql/mssql.pem:/etc/ssl/certs/mssql.pem -v /opt/mssql/mssql.key:/etc/ssl/private/mssql.key" + } + Write-Output "Starting a Docker Container" - Invoke-Expression "docker run --name=`"sql`" -e `"ACCEPT_EULA=Y`"-e `"SA_PASSWORD=$SaPassword`" -e `"MSSQL_PID=Express`" --health-cmd=`"/opt/mssql-tools/bin/sqlcmd -C -S localhost -U sa -P '$SaPassword' -Q 'SELECT 1' -b -o /dev/null`" --health-start-period=`"10s`" --health-retries=3 --health-interval=`"10s`" -p 1433:1433 -d `"mcr.microsoft.com/mssql/server:$Version-latest`"" + Invoke-Expression "docker run --name=`"sql`" -e `"ACCEPT_EULA=Y`"-e `"SA_PASSWORD=$SaPassword`" -e `"MSSQL_PID=Express`" --health-cmd=`"/opt/mssql-tools/bin/sqlcmd -C -S localhost -U sa -P '$SaPassword' -Q 'SELECT 1' -b -o /dev/null`" --health-start-period=`"10s`" --health-retries=3 --health-interval=`"10s`" -p 1433:1433 $AdditionalContainerConfiguration -d `"mcr.microsoft.com/mssql/server:$Version-latest`"" Wait-ForContainer } @@ -88,9 +112,26 @@ if ("sqlengine" -in $Components) { Write-Host "Configuring SQL Express ..." stop-service MSSQL`$SQLEXPRESS - set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpdynamicports -value '' - set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpport -value 1433 - set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\' -name LoginMode -value 2 + + $InstancePath = "HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver" + $SuperSocketNetLibPath = "$InstancePath\supersocketnetlib" + set-itemproperty -path "$SuperSocketNetLibPath\tcp\ipall" -name tcpdynamicports -value '' + set-itemproperty -path "$SuperSocketNetLibPath\tcp\ipall" -name tcpport -value 1433 + set-itemproperty -path $InstancePath -name LoginMode -value 2 + + # SOURCE: https://blogs.infosupport.com/configuring-sql-server-encrypted-connections-using-powershell/ + if ($ForceEncryption) { + Write-Output "Force encryption is set, configuring SQL server to do so ..." + + $params = @{ + DnsName = 'sql1.contoso.com' + CertStoreLocation = 'Cert:\LocalMachine\My' + } + $Certificate = New-SelfSignedCertificate @params + + Set-ItemProperty $SuperSocketNetLibPath -Name "Certificate" -Value $Certificate.Thumbprint.ToLowerInvariant() + Set-ItemProperty $SuperSocketNetLibPath -Name "ForceEncryption" -Value 1 + } Write-Host "Starting SQL Express ..." start-service MSSQL`$SQLEXPRESS diff --git a/test.ps1 b/test.ps1 index 439bb99..61d01c6 100644 --- a/test.ps1 +++ b/test.ps1 @@ -8,3 +8,34 @@ else { Write-Output "Checking if SQL Server is available ..." & sqlcmd -S 127.0.0.1 -U sa -P $env:SA_PASSWORD -Q "SELECT 1" + +Write-Output "Check status of connection encryption ..." + +$sqlQuery = @" +SELECT +session_id, +encrypt_option +FROM sys.dm_exec_connections +WHERE session_id = @@SPID; +"@ + +$results = sqlcmd -S 127.0.0.1 -U sa -P $env:SA_PASSWORD -Q $sqlQuery -h -1 -W + +if ($env:FORCE_ENCRYPTION -eq "true") { + if ($results -match "TRUE") { + Write-Output "Connection from sqlcmd to the sqlengine appears to be encrypted, as expected!" + } + else { + Write-Error "Connection to SQL server is not encrypted!" + exit 1 + } +} +else { + if ($results -match "TRUE") { + Write-Error "Somehow the connection to the SQL server is encrypted, misconfiguration?" + exit 1 + } + else { + Write-Output "Connection from sqlcmd to the sqlengine appears to not be encrypted, as expected!" + } +}