PowerShell / vscode-powershell

Provides PowerShell language and debugging support for Visual Studio Code
https://marketplace.visualstudio.com/items/ms-vscode.PowerShell
MIT License
1.71k stars 490 forks source link

[Feature Request] Provide quick way to sign PS scripts with the extension #548

Open squawkingVFR opened 7 years ago

squawkingVFR commented 7 years ago

System Details

Issue Description

I am experiencing a problem with...

Attached Logs

Follow the instructions in the README about capturing and sending logs.

daviwil commented 7 years ago

Could be a helpful task, we'll look into it.

daviwil commented 7 years ago

/cc @dotps1 @daGup

So how do you normally sign your scripts, something like this?

$cert = dir cert:currentuser\my\ -CodeSigningCert | select -First 1
$timeStampUrl = "some url"
Set-AuthenticodeSignature ScriptPath.ps1 -IncludeChain All -TimeStampServer $timeStampURL

I could pretty easily expose a task for signing the current script with some configuration parameters to set the cert path, timeStampUrl, etc. I could also try to auto-sign scripts before running them in the debugger. Would that be helpful?

daGup commented 7 years ago

Yah. I was looking at how to do something like this as a vscode task as its like a build step but couldn't get my head around the setup of the task with powershell

I never thought to do it as a dir select first filter though usually just grab 1st entry from the cert array.

On May 31, 2017 4:40 PM, "David Wilson" notifications@github.com wrote:

/cc @dotps1 https://github.com/dotps1 @daGup https://github.com/dagup

So how do you normally sign your scripts, something like this?

$cert = dir cert:currentuser\my\ -CodeSigningCert | select -First 1$timeStampUrl = "some url"Set-AuthenticodeSignature ScriptPath.ps1 -IncludeChain All -TimeStampServer $timeStampURL

I could pretty easily expose a task for signing the current script with some configuration parameters to set the cert path, timeStampUrl, etc. I could also try to auto-sign scripts before running them in the debugger. Would that be helpful?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PowerShell/vscode-powershell/issues/548#issuecomment-305348493, or mute the thread https://github.com/notifications/unsubscribe-auth/APGicuoOijNGRzi9dxGKUoAmKoVutSYuks5r_fpsgaJpZM4MeAud .

daviwil commented 7 years ago

Which specific parameters do you use on Set-AuthenticodeSignature? Do you use -IncludeChain All or just the Path and TimeStampServer parameters?

daGup commented 7 years ago

Normally just path. I just followed what was online. However I do like the parameters you added. Is it something that can be optional input?

On May 31, 2017 4:56 PM, "David Wilson" notifications@github.com wrote:

Which specific parameters do you use on Set-AuthenticodeSignature? Do you use -IncludeChain All or just the Path and TimeStampServer parameters?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PowerShell/vscode-powershell/issues/548#issuecomment-305350729, or mute the thread https://github.com/notifications/unsubscribe-auth/APGicpwSNgZqf7D3qHgts-Pn38uy7BEHks5r_f47gaJpZM4MeAud .

daviwil commented 7 years ago

Yep, it's possible to make those configurable.

rkeithhill commented 7 years ago

@daGup I'm curious if you would you prefer a mini-build/release process that does the script signing for you - kind of like the task you were trying to set up?

One issue with signing scripts is that you often don't want to sign the "original" script especially if it is checked into source control. It's much easier to develop & debug against a unsigned script and then sign the script when you're ready to release it. And if you ever put your source up on something like GitHub, you don't want the checked in script(s) to be signed since folks who fork your repo won't have your code-signing cert.

Ideally, I'd like to see a build system that could be configured to do as little or as much as you need. Something where perhaps all you need to do to get your script(s) signed is to fill out a few settings in a file (build.settings.ps1) e.g.:

    # ----------------------- Basic properties --------------------------------

    # The root directory containing scripts to process
    $SrcRootDir  = "$PSScriptRoot"

    # ------------------- Script signing properties ---------------------------

    # Set to $true if you want to sign your scripts. You will need to have a code-signing certificate.
    # You can specify the certificate's subject name below. If not specified, you will be prompted to
    # provide either a subject name or path to a PFX file.  After this one time prompt, the value will
    # saved for future use and you will no longer be prompted.
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
    $ScriptSigningEnabled = $false

    # Specify the Subject Name of the certificate used to sign your scripts.  Leave it as $null and the
    # first time you build, you will be prompted to enter your code-signing certificate's Subject Name.
    # This variable is used only if $SignScripts is set to $true.
    #
    # This does require the code-signing certificate to be installed to your certificate store.  If you
    # have a code-signing certificate in a PFX file, install the certificate to your certificate store
    # with the command below. You may be prompted for the certificate's password.
    #
    # Import-PfxCertificate -FilePath .\myCodeSigingCert.pfx -CertStoreLocation Cert:\CurrentUser\My
    #
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
    $CertSubjectName = $null

Then press Ctrl+Shift+B and the build copies your scripts to another folder (release or out) where they get signed.

daGup commented 7 years ago

That's awesome while reading this on my phone. I'll punch it up on pc in a bit. Trying to implement something as easy for me to force down people's throats. There wasn't much code signing being done so I'm hoping to get this implemented. I really like the build process you've outlined.

On May 31, 2017 5:29 PM, "Keith Hill" notifications@github.com wrote:

@daGup https://github.com/dagup I'm curious if you would you prefer a mini-build/release process that does the script signing for you - kind of like the task you were trying to set up?

One issue with signing scripts is that you often don't want to sign the "original" script especially if it is checked into source control. It's much easier to develop & debug against a unsigned script and then sign the script when you're ready to release it. And if you ever put your source up on something like GitHub, you don't want the checked in script(s) to be signed since folks who fork your repo won't have your code-signing cert.

Ideally, I'd like to see a build system that could be configured to do as little or as much as you need. Something where perhaps all you need to do to get your script(s) signed is to fill out a few settings in a file (build.settings.ps1) e.g.:

# ----------------------- Basic properties --------------------------------

# The root directory containing scripts to process
$SrcRootDir  = "$PSScriptRoot"

# ------------------- Script signing properties ---------------------------

# Set to $true if you want to sign your scripts. You will need to

have a code-signing certificate.

You can specify the certificate's subject name below. If not

specified, you will be prompted to

provide either a subject name or path to a PFX file. After this

one time prompt, the value will

saved for future use and you will no longer be prompted.

[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments',

'')] $ScriptSigningEnabled = $false

# Specify the Subject Name of the certificate used to sign your

scripts. Leave it as $null and the

first time you build, you will be prompted to enter your

code-signing certificate's Subject Name.

This variable is used only if $SignScripts is set to $true.

#
# This does require the code-signing certificate to be installed

to your certificate store. If you

have a code-signing certificate in a PFX file, install the

certificate to your certificate store

with the command below. You may be prompted for the

certificate's password. #

Import-PfxCertificate -FilePath .\myCodeSigingCert.pfx

-CertStoreLocation Cert:\CurrentUser\My # [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] $CertSubjectName = $null

Then press Ctrl+Shift+B and the build copies your scripts to another folder (release or out) where they get signed.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PowerShell/vscode-powershell/issues/548#issuecomment-305355442, or mute the thread https://github.com/notifications/unsubscribe-auth/APGicj4kUVKu5HJ4uPYAgkyl6CD5wVEbks5r_gYFgaJpZM4MeAud .

olathemike commented 7 years ago

I am not clear on where this stands, but I think code signing is becoming a big deal and will continue to increase in importance over time. We are now white-listing our scripts and exes and we have a choice between getting the chksum for each item we want to allow to run and entering that into the white listing tool (ouch), or using signed code (everything signed with one of a few certificates). I really prefer the editor and capabilities of VS Code, but debugging is tough when you can't setup a sign on save.

rkeithhill commented 7 years ago

I'm not opposed to putting this functionality into the VSCode PowerShell extension. I just think that most folks would find an extension provided mechanism rather limiting. OTOH when built into their build process can they customize to fit their particular needs.

You can use VSCode's task runner to kick off a build using either psake or InvokeBuild. That build process can do various things including copying your source into an output folder where it can be signed and can be easily .gitignored. You don't develop/commit signed source, do you?

Then you can setup a debug config to debug the signed scripts from the output folder. This is something you can do today without waiting on the extension to add such a feature. Here's a very simple psake script - build.ps1 (not tested but should give you the idea):

Properties {
    $OutDir = "$PSScriptRoot\out"
}

Task default -depends Build

Task Build -depends Init, Clean, Sign {}

Task Init -requiredVariables OutDir {
    if (!(Test-Path $OutDir)) {
        $null = New-Item $OutDir -ItemType Directory
    }
}

Task Clean -requiredVariables OutDir {
    if (Test-Path $OutDir) {
        Remove-Item $OutDir\* -Recurse -Force
    }
}

Task Sign -requiredVariables OutDir {
    # Modify this line to copy your scripts into the out dir
    Copy-Item $PSScriptRoot\src\* $OutDir -Recurse

    # Grab your cert and sign the scripts in the out dir.
    $cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | 
        Where-Object Subject -match CompanyNane | 
        Sort-Object NotAfter -Descending | Select -First 1
    foreach ($script in (Get-ChildItem $OutDir -File *.ps*1)) {
        Set-AuthenticodeSignature -LiteralPath $script.FullName -Cert $cert
    }
}

Install psake with Install-Module psake -Scope CurrentUser. Then configure your .vscode\tasks.json file like so:

{
    "version": "2.0.0",

    // Start PowerShell
    "windows": {
        "command": "${env:windir}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe",
        "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ]
    },
    "linux": {
        "command": "/usr/bin/powershell",
        "args": [ "-NoProfile" ]
    },
    "osx": {
        "command": "/usr/local/bin/powershell",
        "args": [ "-NoProfile" ]
    },

    // Show the output window always
    "showOutput": "always",

    // Associate with test task runner
    "tasks": [
        {
            "taskName": "Clean",
            "suppressTaskName": true,
            "showOutput": "always",
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Clean;",
                "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }"
            ]
        },
        {
            "taskName": "Build",
            "suppressTaskName": true,
            "isBuildCommand": true,
            "showOutput": "always",
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;",
                "Invoke-Command { Write-Host 'Completed Build task in task runner.' }"
            ]
        }
        {
            "taskName": "Test",
            "suppressTaskName": true,
            "isTestCommand": true,
            "showOutput": "always",
            "args": [
                "Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
                "Invoke-Command { Write-Host 'Completed Test task in task runner.' }"
            ],
            "problemMatcher": "$pester"
        }
    ]
}

You should be able to "build" with a press of Ctrl+Shift+B. Then set up your debug config to launch a script from the out dir.

Of, if you are using Git, add out (or whatever you call it) to your .gitignore file.

olathemike commented 7 years ago
    Great suggestion, I will check it out. Thanks so much for detailing this out for me!

    Get Outlook for iOS

On Mon, Jul 10, 2017 at 3:36 PM -0500, "Keith Hill" notifications@github.com wrote:

I'm not opposed to putting this functionality into the VSCode PowerShell extension. I just think that most folks would find an extension provided mechanism rather limiting. OTOH when built into their build process can they

You can use VSCode's task runner to kick off a build using either psake or InvokeBuild. That build process can do various things including copying your source into an output folder where it can be signed and can be easily .gitignored. You don't develop/commit signed source, do you?

Then you can setup a debug config to debug the signed scripts from the output folder. This is something you can do today without waiting on the extension to add such a feature. Here's a very simple psake script (not tested but should give you the idea): Properties { $OutDir = "$PSScriptRoot\out" }

Task default -depends Build

Task Build -depends Init, Clean, Sign {}

Task Init -requiredVariables OutDir { if (!(Test-Path $OutDir)) { $null = New-Item $OutDir -ItemType Directory } }

Task Clean -requiredVariables OutDir { if (Test-Path $OutDir) { Remove-Item $OutDir* -Recurse -Force } }

Task Sign -requiredVariables OutDir {

Modify this line to copy your scripts into the out dir

Copy-Item $PSScriptRoot\src\* $OutDir -Recurse

# Grab your cert and sign the scripts in the out dir.
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | 
    Where-Object Subject -match CompanyNane | 
    Sort-Object NotAfter -Descending | Select -First 1
foreach ($script in (Get-ChildItem $OutDir -File *.ps*1)) {
    Set-AuthenticodeSignature -LiteralPath $script.FullName -Cert $cert
}

}

Install psake with Install-Module psake -Scope CurrentUser.

Then configure your .vscode\tasks.json file like so: { "version": "2.0.0",

// Start PowerShell
"windows": {
    "command": "${env:windir}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe",
    "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ]
},
"linux": {
    "command": "/usr/bin/powershell",
    "args": [ "-NoProfile" ]
},
"osx": {
    "command": "/usr/local/bin/powershell",
    "args": [ "-NoProfile" ]
},

// Show the output window always
"showOutput": "always",

// Associate with test task runner
"tasks": [
    {
        "taskName": "Clean",
        "suppressTaskName": true,
        "showOutput": "always",
        "args": [
            "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Clean;",
            "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }"
        ]
    },
    {
        "taskName": "Build",
        "suppressTaskName": true,
        "isBuildCommand": true,
        "showOutput": "always",
        "args": [
            "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;",
            "Invoke-Command { Write-Host 'Completed Build task in task runner.' }"
        ]
    }
    {
        "taskName": "Test",
        "suppressTaskName": true,
        "isTestCommand": true,
        "showOutput": "always",
        "args": [
            "Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
            "Invoke-Command { Write-Host 'Completed Test task in task runner.' }"
        ],
        "problemMatcher": "$pester"
    }
]

}

You should be able to "build" with a press of Ctrl+Shift+B. Then set up your debug config to launch a script from the out dir.

Of, if you are using Git, add out (or whatever you call it) to your .gitignore file.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

kensel commented 6 years ago

If the signing can be added to the tasks.json and be able to run on the script real quickly as I use VS Code to edit ps1 files exclusively and they aren't necessarily all in a git repo. I think it would be useful to provide a quick ctrl + shift + P or other hot key and then document a more extensive build process. This way quick edits to files that aren't in a structured format could be signed quickly and there is a process for more detailed build processes.

I don't use the build process enough to know the best ways to set it up and all the ways to pass stuff through. Like can the user settings be passed to the build process for example having a cert thumbprint (or other identifier) be saved in the settings to be passed to build task for the command to use?

I have also updated the tasks.json from above with the current formatting details from reading the intellisense popup.

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558`
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    // Start PowerShell
    "windows": {
        "command": "${env:windir}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe",
        "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ]
    },
    "linux": {
        "command": "/usr/bin/powershell",
        "args": [ "-NoProfile" ]
    },
    "osx": {
        "command": "/usr/local/bin/powershell",
        "args": [ "-NoProfile" ]
    },
    // Show the output window always
    "showOutput": "always",
    // Associate with test task runner
    "tasks": [
        {
            "label": "Clean",
            "group":  "none",
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared"
            },
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Clean;",
                "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }"
            ]
        },
        {
            "label": "Build",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared"
            },
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;",
                "Invoke-Command { Write-Host 'Completed Build task in task runner.' }"
            ]
        },
        {
            "label": "Test",
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared"
            },
            "args": [
                "Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
                "Invoke-Command { Write-Host 'Completed Test task in task runner.' }"
            ],
            "problemMatcher": "$pester"
        }
    ]
}
rkeithhill commented 6 years ago

@kensel That tasks.json is a bit out-of-date (still uses sysnative & powershell instead of pwsh on Linux/OSX). See this one which is up-to-date: https://github.com/PowerShell/vscode-powershell/blob/master/examples/.vscode/tasks.json

You can easily add a task to sign the active file. It requires that you encode a way to locate the code-signing cert that you want to use e.g.:

        {
            "label": "Sign",
            "type": "shell",
            "command": "$subject = 'your cert subject name'; $cert = @(Get-ChildItem Cert:/CurrentUser -Recurse -CodeSigningCert | Where-Object Subject -match $subject | Where-Object NotAfter -gt (Get-Date)); Set-AuthenticodeSignature ${file} $cert[0]"
        },

BTW the issue with sysnative is that the more popular version of VSCode is 64-bit and sysnative isn't recognized in a 64-bit process.

TylerLeonhardt commented 6 years ago

@rkeithhill do you think we should add such a task to the tasks.json?

rkeithhill commented 6 years ago

The problem with the task is it requires input/modification. It requires info to pick a code signing cert when there is more than one on the machine. That is pretty common for my machines.

If we made this an extension command, we could present a pick list of code-signing certs if we find more than one valid code-signing cert.

fullenw1 commented 4 years ago

Is somebody still working on this feature request? :)

TylerLeonhardt commented 4 years ago

Not a high priority for the PowerShell team at the moment - too busy on performance & stability.

However, we're open source so if you're interested in contributing, we'd love to work with you! Although we should understand what scenario we are specifically targeting here.

DEberhardt commented 4 years ago

Found this, which does the job for me. Hope it helps :) https://stackoverflow.com/questions/39730482/digitally-signing-scripts-in-vscode