Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): Add npipe support #2018

Merged
merged 3 commits into from Jul 20, 2018

Conversation

olljanat
Copy link
Contributor

@olljanat olljanat commented Jul 3, 2018

Updated PR for which replaces #1186 and solves merge issues on it.

Waiting for microsoft/go-winio#80 to be merged.

Closes #1179

Closes #1728

@deviantony
Copy link
Member

Great, thanks @olljanat

@deviantony deviantony mentioned this pull request Jul 4, 2018
@deviantony deviantony added the work-in-progress Indicates that the PR is a work-in-progress and not ready yet label Jul 4, 2018
@seal-ss
Copy link

seal-ss commented Jul 4, 2018

Woot 🎉

@olljanat
Copy link
Contributor Author

olljanat commented Jul 4, 2018

I published docker image for testing purposes which contains these changes . You can test it with command:

docker run -v \\.\pipe\docker_engine:\\.\pipe\docker_engine -p 9000:9000 -d ollijanatuinen/portainer:windows1803-amd64-npipe-1

Implementation is now done on way that when you add local Docker env you just need enable "Windows containers" switch on there:
portainer_win_local_endpoint

NOTE! Currently that image it is build from my debug branch which also contains DialPipe workaround olljanat@0c564d7 and where I have rollbacked 61c285b as it looks to be broken (at least l always got "Unable to locate template file on disk" error with it).

Test results

  • Windows 10, Version 1803 + Docker 18.03.1-ce-win65 - works fine
  • Windows Server, Version 1803 + 18.03.1-ee-1 - gives me error:
2018/07/04 22:50:22 http error: Unable to proxy the request via the Docker socket (err=open //./pipe/docker_engine: Access is denied.) (code=500)

If I try without mapping npipe I got error like this:

2018/07/05 00:21:32 http error: Unable to proxy the request via the Docker socket (err=open //./pipe/docker_engine: The system cannot find the file specified.) (code=500

so it looks that npipe mapping works even on server version but I misses some permissions.

@StefanScherer any ideas what would be missing?

Other known issues currently:

  • If you add local connection without selecting Windows containers. It will be saved with incorrect settings even if you change it.
  • Swarm mode does not work correctly. Any access to services gives very weird errors.

@deviantony
Copy link
Member

Great job @olljanat 👍

If you add local connection without selecting Windows containers. It will be saved with incorrect settings even if you change it.

An communication error should be raised as it is now when adding a local Unix environment when the socket file is not available.

@StefanScherer
Copy link
Contributor

@olljanat Awesome!
The permission denied error may be related to non-admin user in NanoServer image.
Have you tried with this additional Dockerfile instruction?

USER ContainerAdministrator

I know Microsoft wants to deprecated this, but sometimes it's the only option.

@deviantony
Copy link
Member

@olljanat

I have rollbacked 61c285b as it looks to be broken (at least l always got "Unable to locate template file on disk" error with it

Do you mind rebasing this PR on top of develop with #2024 to check if that template file issue is fixed?

@olljanat
Copy link
Contributor Author

olljanat commented Jul 5, 2018

@StefanScherer yes "USER ContainerAdministrator" did the trick. Thanks.
@deviantony #2024 fixed template issue. Thanks

There is also now new version of Docker image ollijanatuinen/portainer:windows1803-amd64-npipe-2

Needs more testing but looks quite good.

@seal-ss
Copy link

seal-ss commented Jul 5, 2018

@olljanat Ha, thanks good to know. I'll try your image soon. Do you encounter problems on 1803 that still requires the go-winio fixes?

@olljanat
Copy link
Contributor Author

olljanat commented Jul 5, 2018

@seal-ss your DialPipe workaround is currently included to image just in case.

Only bigger issue which I can see now is that DockerHub authentication fails and that why image pull or service create features cannot be used but I'm investigating it.

@olljanat
Copy link
Contributor Author

olljanat commented Jul 5, 2018

Docker pull and swarm features works on latest ollijanatuinen/portainer:windows1803-amd64-npipe-3 image.

@deviantony can you review? Only known issue left is that if user try use npipe on Linux it will generate http panic errors to log but I'm not sure what is correct way to fix them?

2018/07/05 13:49:50 http: panic serving 172.17.0.1:3744: runtime error: invalid memory address or nil pointer dereference
goroutine 31 [running]:
net/http.(*conn).serve.func1(0xc420429540)
        /usr/local/go/src/net/http/server.go:1697 +0xd0
panic(0xbdce00, 0x117ec70)
        /usr/local/go/src/runtime/panic.go:491 +0x283
github.com/portainer/portainer/http/proxy.(*proxyTransport).proxyDockerRequest(0x0, 0xc4202fa600, 0x0, 0xc42041e4c0, 0xc420069810)
        /go/src/github.com/portainer/portainer/http/proxy/docker_transport.go:66 +0x9a
github.com/portainer/portainer/http/proxy.(*localProxy).ServeHTTP(0xc420086038, 0x1138cc0, 0xc4202f4000, 0xc4202fa600)
        /go/src/github.com/portainer/portainer/http/proxy/local.go:22 +0x9d
net/http.StripPrefix.func1(0x1138cc0, 0xc4202f4000, 0xc4202fa500)
        /usr/local/go/src/net/http/server.go:1957 +0x1a5
net/http.HandlerFunc.ServeHTTP(0xc420420870, 0x1138cc0, 0xc4202f4000, 0xc4202fa500)
        /usr/local/go/src/net/http/server.go:1918 +0x44
github.com/portainer/portainer/http/handler/endpointproxy.(*Handler).proxyRequestsToDockerAPI(0xc420010d50, 0x1138cc0, 0xc4202f4000, 0xc4202fa500, 0                                                                                         x84739b)
        /go/src/github.com/portainer/portainer/http/handler/endpointproxy/proxy_docker.go:41 +0x480
github.com/portainer/portainer/http/handler/endpointproxy.(*Handler).(github.com/portainer/portainer/http/handler/endpointproxy.proxyRequestsToDocke                                                                                         rAPI)-fm(0x1138cc0, 0xc4202f4000, 0xc4202fa500, 0xc420420810)
        /go/src/github.com/portainer/portainer/http/handler/endpointproxy/handler.go:28 +0x48
github.com/portainer/portainer/http/error.LoggerHandler.ServeHTTP(0xc420141b90, 0x1138cc0, 0xc4202f4000, 0xc4202fa500)
        /go/src/github.com/portainer/portainer/http/error/error.go:24 +0x44
github.com/portainer/portainer/http/security.(*RequestBouncer).mwCheckAuthentication.func1(0x1138cc0, 0xc4202f4000, 0xc4202fa400)
        /go/src/github.com/portainer/portainer/http/security/bouncer.go:202 +0xd7
net/http.HandlerFunc.ServeHTTP(0xc4203b2960, 0x1138cc0, 0xc4202f4000, 0xc4202fa400)
        /usr/local/go/src/net/http/server.go:1918 +0x44
github.com/portainer/portainer/http/security.mwSecureHeaders.func1(0x1138cc0, 0xc4202f4000, 0xc4202fa400)
        /go/src/github.com/portainer/portainer/http/security/bouncer.go:119 +0xfd
net/http.HandlerFunc.ServeHTTP(0xc4203b2980, 0x1138cc0, 0xc4202f4000, 0xc4202fa400)
        /usr/local/go/src/net/http/server.go:1918 +0x44
github.com/gorilla/mux.(*Router).ServeHTTP(0xc42017d340, 0x1138cc0, 0xc4202f4000, 0xc4202fa400)
        /go/src/github.com/gorilla/mux/mux.go:162 +0xed
net/http.StripPrefix.func1(0x1138cc0, 0xc4202f4000, 0xc4202fa000)
        /usr/local/go/src/net/http/server.go:1957 +0x1a5
net/http.HandlerFunc.ServeHTTP(0xc4204202d0, 0x1138cc0, 0xc4202f4000, 0xc4202fa000)
        /usr/local/go/src/net/http/server.go:1918 +0x44
github.com/portainer/portainer/http/handler.(*Handler).ServeHTTP(0xc4203c58c0, 0x1138cc0, 0xc4202f4000, 0xc4202fa000)
        /go/src/github.com/portainer/portainer/http/handler/handler.go:62 +0xacb
net/http.serverHandler.ServeHTTP(0xc4202340d0, 0x1138cc0, 0xc4202f4000, 0xc4202fa000)
        /usr/local/go/src/net/http/server.go:2619 +0xb4
net/http.(*conn).serve(0xc420429540, 0x11398c0, 0xc42018c3c0)
        /usr/local/go/src/net/http/server.go:1801 +0x71d
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:2720 +0x288

@olljanat olljanat changed the title WIP: feat(api): Add npipe support feat(api): Add npipe support Jul 5, 2018
@deviantony
Copy link
Member

@olljanat I'll try to review this weekend and give you indications about the npipe on Linux issue.

@olljanat
Copy link
Contributor Author

olljanat commented Jul 9, 2018

@deviantony ping

@deviantony
Copy link
Member

Sorry @olljanat I'm a bit busy, I'll do a review tomorrow.

@olljanat
Copy link
Contributor Author

olljanat commented Jul 9, 2018

No worries. I also found and fixed one new bug and did plenty of more testing.

There is now docker image ollijanatuinen/portainer:windows1803-amd64-npipe-4 build directly from this branch without DialPipe workaround and same version Windows binaries can be found from: https://github.com/olljanat/portainer/files/2177891/portainer-with-npipe-support-pre4.zip

@seal-ss I did quite lot of testing with this one I was not able to repeat that old issue easily. I did see that once on Win 10 but after restart it disappeared and I have not ever seen that with Windows Server, version 1803.

So IMO we can merge this even before DialPipe fix merged.

@StefanScherer
Copy link
Contributor

@olljanat That looks awesome! I've started your image in a Windows Server 1803 swarm (but currently just a docker run -d)
Sometimes I see problems when I switch to the dashboard: Failure
Unable to proxy the request via the Docker socket
But all the other views (Container, Services, Images, ...) work without a problem.

Another approach may be to retry the request for the several dashboard API calls to make it more stable.

@olljanat
Copy link
Contributor Author

True. It happens only time to time and I only see that on dashboard but it is still there.

Now there is also test version ollijanatuinen/portainer:winio-pr80 which contains content on this PR + proposed fix from winio.

With that one I was not able duplicate that issue anymore.

@StefanScherer
Copy link
Contributor

@olljanat Yes, this image works stable even for the dashboard 🎉

@deviantony deviantony added contrib/tech-review-in-progress and removed work-in-progress Indicates that the PR is a work-in-progress and not ready yet labels Jul 10, 2018
Copy link
Member

@deviantony deviantony left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heya @olljanat

I did a first review, have a look at my comments. One thing though, could you rebase the PR on the latest version of the develop branch ? It'll be easier to review it.

Thanks !

@@ -167,19 +167,30 @@ func createDial(endpoint *portainer.Endpoint) (net.Conn, error) {
var host string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should default to url.Host here by assuming url.Scheme == tcp.

This simplifies the if/else statement below.

host := url.Host

if url.Scheme == "unix" || url.Scheme = "npipe" {
host = url.Path
}

@@ -1,6 +1,6 @@
package proxy

// unixSocketHandler represents a handler to proxy HTTP requests via a unix:// socket
// represents a handler to proxy HTTP requests via a unix:// socket and npipe:// named pipes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can actually remove that comment.

@@ -51,6 +51,9 @@ func (manager *Manager) createDockerProxy(endpointURL *url.URL, tlsConfig *porta
}
return manager.proxyFactory.newDockerHTTPProxy(endpointURL, false), nil
}
if endpointURL.Scheme == "npipe" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we can assume npipe:// or unix:// here depending on the platform, no need for that check.

Simply use:

return manager.proxyFactory.newLocalProxy(endpointURL.Path), nil

See my comment above regarding the local_unix.go and local_windows.go files.

"github.com/Microsoft/go-winio"
)

func (factory *proxyFactory) newNamedPipeProxy(path string) http.Handler {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be renamed newLocalProxy.

@@ -0,0 +1,33 @@
// +build windows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File should be renamed local_windows.go.

"net/http"
)

func (factory *proxyFactory) newNamedPipeProxy(path string) http.Handler {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should be renamed newLocalProxy and it's content should be the actual content of newDockerSocketProxy (relocate the function here and rename it).

@@ -9,6 +9,57 @@
<span class="setting" ng-class="{ 'setting-active': $ctrl.state.displayTextFilter }" ng-click="$ctrl.updateDisplayTextFilter()" ng-if="$ctrl.showTextFilter">
<i class="fa fa-search" aria-hidden="true"></i> Search
</span>
<span class="setting" ng-class="{ 'setting-active': $ctrl.columnVisibility.state.open }" uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.columnVisibility.state.open">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rebase your PR on the latest develop branch? It will be easier to review if I can only focus on the changes related to this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. My bad.

@@ -54,10 +54,15 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) {
return Endpoints.remove({id: endpointID}).$promise;
};

service.createLocalEndpoint = function() {
service.createLocalEndpoint = function(Windows) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename the parameter to useNamedPipe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. If we assume npipe:// or unix:/// based on OS then we can remove this parameter and whole Windows/Linux containers selection.

I will change it like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if Portainer is running on Windows, then creating a local endpoint should default to use npipe. If running on Linux, default to use unix socket.

This is totally doable in the backend as we can create platform specific files (see my comment about local_windows.go and local_linux.go above).

But the frontend is not aware of the platform where Portainer is running. that's why #1186 introduced a Local boolean parameter in the endpoint creation payload.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I will re-check the original implementation

@@ -1,8 +1,10 @@
FROM microsoft/nanoserver
ARG SOURCE_TAG=latest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of adding this ARG layer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed that to be able build Windows Server, version 1803 compatible version on Win 10 (docker build . --build-arg SOURCE_TAG=1803) other why it defaults to Windows Server 2016 based version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seal-ss can I still use your rebase-docker-image tool to build a 1803 image? If so, I believe that this line is not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably. I was just not familiar with that tool. I can test and remove ARG layer if that works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah our build workflow is not public yet, I still have all the docs on my side. I can give you more details on Slack at https://portainer.io/slack/, ping me there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the rebase-docker-image tool still should work for your COPY deployment builds.


VOLUME C:\\data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose to remove the default VOLUME statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find out on Windows Server, version 1803 where it tested these images using Docker process isolation mode that if VOLUME statement is on Dockerfile Portainer does not see C:\Data folder and wants create that but Windows things that it is there and folder creating fails. That that fails whole Portainer start process. Without that line it works correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seal-ss are you aware of any issues using the VOLUME statement in Windows server 1803?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I did now some more testing and found that USER ContainerAdministrator line actually fixes also this issue.
If that line is not on file and VOLUME statement is starting portainer gives error like this:
2018/07/11 10:30:14 mkdir C:\data: Cannot create a file when that file already exists.

So I will restore that line to file.

@deviantony
Copy link
Member

deviantony commented Jul 13, 2018

@StefanScherer thanks for the update and the information, I was not sure about this.

Yes, the portainer images embeds the Docker binary in order to manage Swarm stacks.

@olljanat Compose stack management on a standalone Docker host still need to be tested, as I am not sure about the usage of npipe with libcompose.

@olljanat
Copy link
Contributor Author

@deviantony yes. Both compose version 2 and swarm stack deployments works like same way like than on 1.18.1. No new bug generated by this changes but I found one Windows containers related bug which already exists on 1.18.1 + TCP (will create old issue from that one).

Named Pipe support for Windows containers have been there from day 1 as that it way how docker cli communicates with docker daemon. Feature which we use here is ability mount Named Pipe inside of container and that requires at least Windows 10/Server, version 1709 + Docker 17.09.

But older versions can still use old way to communicate over TCP 2375.

So this one is ready for merge when winio fix is merged.

@deviantony
Copy link
Member

Great, let's wait for the merge of microsoft/go-winio#80 then.

@ragaar
Copy link

ragaar commented Jul 16, 2018

Just to confirm, Will the final command look something like this?
docker run -d -p 9000:9000 -u ContainerAdministrator --name portainer --restart always -v \\.\pipe\docker_engine:\\.\pipe\docker_engine portainer/portainer

@olljanat
Copy link
Contributor Author

@ragaar -u parameter is not need because it is set inside of Dockerfile and you most probably want include data folder mount too like it is on http://portainer.readthedocs.io/en/stable/deployment.html#persist-portainer-data

so result will be

docker run -d -p 9000:9000 --name portainer --restart always -v \\.\pipe\docker_engine:\\.\pipe\docker_engine -v C:\ProgramData\Portainer:C:\data portainer/portainer

We hopefully get this one included to release which is scheduled to next week.

@deviantony deviantony added this to the 1.18.x milestone Jul 18, 2018
@@ -1,5 +1,7 @@
FROM microsoft/nanoserver

USER ContainerAdministrator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question, will this statement cause any issue with current windows images or 1709 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I just quickly tested that docker run -t --rm -u ContainerAdministrator microsoft/nanoserver:1709 cmd /c echo %USERNAME% command works just fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And on microsoft/nanoserver:sac2016 it actually does not change anything as ContainerAdministrator is default setting on that version (but also does not break anything).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, still fine for 1709 and 1803 windows images.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks guys

@olljanat
Copy link
Contributor Author

I noticed that this PR was not fully compatible with #2033 so I added one more commit to fix that issue.

Copy link
Member

@deviantony deviantony left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@deviantony
Copy link
Member

microsoft/go-winio#80 has been merged, I'll have a look later today to merge this one.

@deviantony deviantony merged commit 4129550 into portainer:develop Jul 20, 2018
@deviantony
Copy link
Member

Thanks to all the people involved !

@olljanat @StefanScherer 👍

@deviantony
Copy link
Member

@olljanat @StefanScherer one of our user reported the following:

Hi, I am trying to run Portainer on a Windows 10 (Also tried on Windows Server 2016) but I keep getting this message on both the docker environments:

docker : C:\Program Files\Docker\Docker\Resources\bin\docker.exe: Error response from daemon: container d20b96fb14e43e112d6a660a23e84853fd90ca602259933fcdabcf941a74789a encountered an error during CreateProcess: failure in a Windows system call: The user name or password is incorrect. (0x52e) extra info: {"CommandLine":"/portainer.exe","User":"ContainerAdministrator","WorkingDirectory":"C:\\","CreateStdInPipe":true,"CreateStdOutPipe":true,"CreateStdErrPipe":true,"ConsoleSize":[0,0]}. At line:1 char:1 
docker run -d -p 9000:9000 --name portainer -v C:\ProgramData\docker\ ... 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
CategoryInfo : NotSpecified: (C:\Program File...leSize":[0,0]}.:String) [], RemoteException 
FullyQualifiedErrorId : NativeCommandError 

I am running this docker command:

docker volume create portainer_data 
docker run -d -p 9000:9000 --name portainer -v C:\ProgramData\docker\Containers\Portainer:C:\data portainer/portainer 

I also tried with --no-auth but I still get the same error. Maybe you can help.

Any pointers on what might be the cause?

@ragaar
Copy link

ragaar commented Jul 31, 2018

@deviantony
Doesn't look like it's tied to Window's pipe-engine. So, you might create a new issue. Though, I think what you're encountering could be referenced in the documentation.

I'd simplify the command you're running down to minimum inputs required and scale up from there to figure out which command is causing the error.

Also, just for reference you defined a data volume but to use it your volume command would look more like this: -v portainer_data:drive:\path\in\container.

Full disclosure: I've not done much with volumes in Windows, so I can't confirm the direction of the \ from memory. But that problem, I know, is easy to find online :)

@deviantony
Copy link
Member

I did not actually execute these commands, someone reported it to us via e-mail :-)

I do not have a Windows environment on which I can try to reproduce at the moment. So I'm asking for any help here.

@ragaar
Copy link

ragaar commented Jul 31, 2018

Ahhh! That makes more sense. I don't have Windows either! lol

@olljanat
Copy link
Contributor Author

@deviantony I just tested on Win 10 that both Linux and Windows containers works with commands given on here http://portainer.readthedocs.io/en/stable/deployment.html#windows except that there is small typo which I fixed on portainer/portainer-docs#54

Windows Server 2016 need still follow old documentation: http://portainer.readthedocs.io/en/stable/faq.html#how-can-i-setup-portainer-on-windows-server-2016

@deviantony
Copy link
Member

@olljanat as you can see the user is simply running:

docker run -d -p 9000:9000 --name portainer -v C:\ProgramData\docker\Containers\Portainer:C:\data portainer/portainer 

Which should be working on Win server 2016

@olljanat
Copy link
Contributor Author

Yep but npipe is not used until user selects local connection.

So ask him open new issue with all details.

@deviantony
Copy link
Member

Ok, I'll ask him to open an issue. Thanks @olljanat

xAt0mZ pushed a commit that referenced this pull request Aug 25, 2022
* apply style to authentication logs and activity logs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants